vgrep: a visual grep

Richard Suchenwirth 2011-08-14 - After a long time, here's a little weekend fun project again: a tool with which to search a text file. Call it like

  vgrep.tcl myfile.txt &

and get a window with an entry, into which a query can be typed, and a scrolled text widget which displays the matching lines.

The query is processed at each key press, independent of case, and with .* substituted for blanks, so

 the bat

matches both

 TheBat_1929.mp4;...
 the_batmobile_1988.ogv;...

The number of hits is displayed in the title bar. I needed this thingy to search my film collection, and it performs reasonably well with a file of some 6500 lines on a netbook.
vgrepSS
Screenshot using vgrep on itself...

 #!/usr/bin/env tclsh
 set usage {
    usage: vgrep textfile &
    Show the given textfile. If strings are entered
    into the top entry widget, filter only those lines
    that match (case-indepent, spaces mean .*)
 }
 if {[llength $argv] != 1} {puts stderr $usage; exit 1}
 package require Tk

 proc main argv {
    set font {Arial 12}
    pack [entry .e -font $font] -fill x
    bind .e <KeyRelease> {rewrite .e .t}
    pack [scrollbar .y -command ".t yview"] -side right -fill y
    pack [text .t -wrap none -yscroll ".y set" -font $font] \
        -fill both -side left -expand 1
    .t tag configure blue -foreground blue
    set ::data [readlines [lindex $argv 0]]
    rewrite .e .t
    focus -force .e
 }
 proc rewrite {entry text} {
    global data
    $text delete 1.0 end
    set re [string map {" " .*} [$entry get]]
    foreach line $data {
        if {$re eq "" || [regexp -nocase $re $line]} {
            foreach {file path} [split $line ";"] break
            $text insert end $file black " ; $path\n" blue
        }
    }
    wm title . "[int [$text index end-2c]] hits - vgrep $::argv"
 }
 proc int x {expr {int($x)}}
 proc readlines filename {
    set f [open $filename]
    split [read -nonewline $f][close $f] \n
 }
 main $argv
 bind all <Escape> {exec tclsh $argv0 $argv &; exit}