gitk: Further speedups

Now we don't parse the commits as we are reading them, we just put
commit data on a list as a blob, and instead parse the commit when
we need the various parts of it, such as when a commit is drawn on
the canvas.  This makes searching a bit more interesting: now we
scan through the commit blobs doing a string or regexp match to find
commits that might match, then for those that might match, we parse
the commit info (if it isn't already parsed) and do the matching
for the various fields as before.

Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/gitk b/gitk
index cca9d35..8d7b258 100755
--- a/gitk
+++ b/gitk
@@ -35,6 +35,7 @@
 proc start_rev_list {rlargs} {
     global startmsecs nextupdate ncmupdate
     global commfd leftover tclencoding datemode
+    global commitdata
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
@@ -52,6 +53,7 @@
 	exit 1
     }
     set leftover {}
+    set commitdata {}
     fconfigure $commfd -blocking 0 -translation lf
     if {$tclencoding != {}} {
 	fconfigure $commfd -encoding $tclencoding
@@ -72,11 +74,9 @@
 }
 
 proc getcommitlines {commfd}  {
-    global parents cdate children nchildren
-    global commitlisted commitinfo phase nextupdate
-    global stopped leftover
-    global canv
-    global displayorder commitidx commitrow
+    global commitlisted nextupdate
+    global leftover
+    global displayorder commitidx commitrow commitdata
 
     set stuff [read $commfd]
     if {$stuff == {}} {
@@ -136,12 +136,9 @@
 	}
 	set id [lindex $ids 0]
 	set olds [lrange $ids 1 end]
-	set cmit [string range $cmit [expr {$j + 1}] end]
 	set commitlisted($id) 1
 	updatechildren $id [lrange $ids 1 end]
-	if {![info exists commitinfo($id)]} {
-	    parsecommit $id $cmit 1
-	}
+	lappend commitdata [string range $cmit [expr {$j + 1}] end]
 	set commitrow($id) $commitidx
 	incr commitidx
 	lappend displayorder $id
@@ -269,6 +266,25 @@
 			     $comname $comdate $comment]
 }
 
+proc getcommit {id {row {}}} {
+    global commitdata commitrow commitinfo nparents
+
+    if {$row eq {}} {
+	if {![info exists commitrow($id)]} {return 0}
+	set row $commitrow($id)
+    }
+    if {$row < [llength $commitdata]} {
+	parsecommit $id [lindex $commitdata $row] 1
+    } else {
+	readcommit $id
+	if {![info exists commitinfo($id)]} {
+	    set commitinfo($id) {"No commit information available"}
+	    set nparents($id) 0
+	}
+    }
+    return 1
+}
+
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
     global otherrefids idotherrefs
@@ -1317,6 +1333,7 @@
     global displayorder rowidlist
     global idrowranges idrangedrawn iddrawn
     global commitinfo commitlisted parents numcommits
+    global commitdata
 
     if {$row >= $numcommits} return
     foreach id [lindex $rowidlist $row] {
@@ -1344,11 +1361,7 @@
 	return
     }
     if {![info exists commitinfo($id)]} {
-	readcommit $id
-	if {![info exists commitinfo($id)]} {
-	    set commitinfo($id) {"No commit information available"}
-	    set nparents($id) 0
-	}
+	getcommit $id $row
     }
     assigncolor $id
     if {[info exists commitlisted($id)] && [info exists parents($id)]
@@ -1604,19 +1617,19 @@
 }
 
 proc finishcommits {} {
-    global phase
+    global commitidx phase
     global canv mainfont ctext maincursor textcursor
 
-    if {$phase == "incrdraw"} {
+    if {$commitidx > 0} {
 	drawrest
     } else {
 	$canv delete all
 	$canv create text 3 3 -anchor nw -text "No commits selected" \
 	    -font $mainfont -tags textitems
-	set phase {}
     }
     . config -cursor $maincursor
     settextcursor $textcursor
+    set phase {}
 }
 
 # Don't change the text pane cursor if it is currently the hand cursor,
@@ -1672,7 +1685,8 @@
     global findtype findloc findstring markedmatches commitinfo
     global numcommits displayorder linehtag linentag linedtag
     global mainfont namefont canv canv2 canv3 selectedline
-    global matchinglines foundstring foundstrlen
+    global matchinglines foundstring foundstrlen matchstring
+    global commitdata
 
     stopfindproc
     unmarkmatches
@@ -1689,6 +1703,8 @@
     }
     set foundstrlen [string length $findstring]
     if {$foundstrlen == 0} return
+    regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
+    set matchstring "*$matchstring*"
     if {$findloc == "Files"} {
 	findfiles
 	return
@@ -1700,8 +1716,21 @@
     }
     set didsel 0
     set fldtypes {Headline Author Date Committer CDate Comment}
-    for {set l 0} {$l < $numcommits} {incr l} {
+    set l -1
+    foreach d $commitdata {
+	incr l
+	if {$findtype == "Regexp"} {
+	    set doesmatch [regexp $foundstring $d]
+	} elseif {$findtype == "IgnCase"} {
+	    set doesmatch [string match -nocase $matchstring $d]
+	} else {
+	    set doesmatch [string match $matchstring $d]
+	}
+	if {!$doesmatch} continue
 	set id [lindex $displayorder $l]
+	if {![info exists commitinfo($id)]} {
+	    getcommit $id $l
+	}
 	set info $commitinfo($id)
 	set doesmatch 0
 	foreach f $info ty $fldtypes {
@@ -2069,7 +2098,7 @@
 # mark a commit as matching by putting a yellow background
 # behind the headline
 proc markheadline {l id} {
-    global canv mainfont linehtag commitinfo
+    global canv mainfont linehtag
 
     drawcmitrow $l
     set bbox [$canv bbox $linehtag($l)]
@@ -2725,9 +2754,9 @@
 	set id [string tolower $sha1string]
 	if {[regexp {^[0-9a-f]{4,39}$} $id]} {
 	    set matches {}
-	    for {set l 0} {$l < $numcommits} {incr l} {
-		if {[string match $id* [lindex $displayorder $l]]} {
-		    lappend matches [lindex $displayorder $l]
+	    foreach i $displayorder {
+		if {[string match $id* $i]} {
+		    lappend matches $i
 		}
 	    }
 	    if {$matches ne {}} {
@@ -2755,7 +2784,7 @@
     global hoverx hovery hoverid hovertimer
     global commitinfo canv
 
-    if {![info exists commitinfo($id)]} return
+    if {![info exists commitinfo($id)] && ![getcommit $id]} return
     set hoverx $x
     set hovery $y
     set hoverid $id
@@ -2849,6 +2878,7 @@
 proc lineclick {x y id isnew} {
     global ctext commitinfo children cflist canv thickerline
 
+    if {![info exists commitinfo($id)] && ![getcommit $id]} return
     unmarkmatches
     unselectline
     normalline
@@ -2890,6 +2920,7 @@
 	set i 0
 	foreach child $children($id) {
 	    incr i
+	    if {![info exists commitinfo($child)] && ![getcommit $child]} continue
 	    set info $commitinfo($child)
 	    $ctext insert end "\n\t"
 	    $ctext insert end $child [list link link$i]