Anonymous avatar Anonymous committed 2f85b7e

git-gui: Switch internal blame structure to Tcl lists

The Tcl list datatype is significantly faster to work with than
the array type, especially if our indexes are a consecutive set
of numbers, like say line numbers in a file.

This rather large change reorganizes the internal data structure
of the blame viewer to use a proper Tcl list for the annotation
information about a line. Each line is given its own list within
the larger line_data list, where the indexes correspond to various
facts about that particular line.

The interface does seem to be more responsive this way, with less
time required by Tcl to process blame, and to switch to another
version of the same file. It could just be a placebo effect, but
either way most Tcl experts perfer lists for this type of work over
arrays.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>;

Comments (0)

Files changed (1)

 field total_lines       0  ; # total length of file
 field blame_lines       0  ; # number of lines computed
 field have_commit          ; # array commit -> 1
-field line_commit          ; # array line -> sha1 commit
-field line_file            ; # array line -> file name
+field line_data            ; # list of {commit origfile origline}
 
 field r_commit             ; # commit currently being parsed
 field r_orig_line          ; # original line number
 		set total_lines 0
 		set blame_lines 0
 		array unset have_commit
-		array unset line_commit
-		array unset line_file
 	}
 
 	if {[winfo exists $w.status.c]} {
 	}
 	lappend history [list $commit $path]
 
+	# Index 0 is always empty.  There is never line 0 as
+	# we use only 1 based lines, as that matches both with
+	# git-blame output and with Tk's text widget.
+	#
+	set line_data [list [list]]
+
 	set status "Loading $commit:[escape_path $path]..."
 	$w_path conf -text [escape_path $path]
 	if {$commit eq {}} {
 	while {[gets $fd line] >= 0} {
 		regsub "\r\$" $line {} line
 		incr total_lines
+		lappend line_data {}
 
 		if {$total_lines > 1} {
 			foreach i $w_columns {$i insert end "\n"}
 
 			set first_lno $lno
 			while {
-			   ![catch {set ncmit $line_commit([expr {$first_lno - 1}])}]
-			&& ![catch {set nfile $line_file([expr {$first_lno - 1}])}]
-			&& $ncmit eq $cmit
-			&& $nfile eq $file
+			   $first_lno > 1
+			&& $cmit eq [lindex $line_data [expr {$first_lno - 1}] 0]
+			&& $file eq [lindex $line_data [expr {$first_lno - 1}] 1]
 			} {
 				incr first_lno -1
 			}
 
 			while {$n > 0} {
 				set lno_e "$lno.0 lineend + 1c"
-				if {![catch {set g g$line_commit($lno)}]} {
+				if {[lindex $line_data $lno] ne {}} {
+					set g [lindex $line_data $lno 0]
 					foreach i $w_columns {
 						$i tag remove g$g $lno.0 $lno_e
 					}
 				}
-
-				set line_commit($lno) $cmit
-				set line_file($lno)   $file
+				lset line_data $lno [list $cmit $file]
 
 				$w_cgrp delete $lno.0 "$lno.0 lineend"
 				if {$lno == $first_lno} {
 			}
 
 			while {
-			   ![catch {set ncmit $line_commit($lno)}]
-			&& ![catch {set nfile $line_file($lno)}]
-			&& $ncmit eq $cmit
-			&& $nfile eq $file
+			   $cmit eq [lindex $line_data $lno 0]
+			&& $file eq [lindex $line_data $lno 1]
 			} {
 				$w_cgrp delete $lno.0 "$lno.0 lineend"
 
 
 method _load_commit {pos} {
 	set lno [lindex [split [$w_cgrp index $pos] .] 0]
-	if {[catch {set cmit $line_commit($lno)}]} return
-	if {[catch {set file $line_file($lno)  }]} return
-
-	set commit $cmit
-	set path $file
-	_load $this
+	set dat [lindex $line_data $lno]
+	if {$dat ne {}} {
+		set commit [lindex $dat 0]
+		set path   [lindex $dat 1]
+		_load $this
+	}
 }
 
 method _showcommit {lno} {
 
 	$w_cmit conf -state normal
 	$w_cmit delete 0.0 end
-	if {[catch {set cmit $line_commit($lno)}]} {
+
+	set dat [lindex $line_data $lno]
+	if {$dat eq {}} {
 		set cmit {}
 		$w_cmit insert end "Loading annotation..."
 	} else {
+		set cmit [lindex $dat 0]
+		set file [lindex $dat 1]
+
 		set old_bgcolor [$w_file tag cget g$cmit -background]
 		foreach i $w_columns {
 			$i tag conf g$cmit -background $active_color
 		$w_cmit insert end "$committer_name $committer_email" header_val
 		$w_cmit insert end "$committer_time\n" header_val
 
-		if {$line_file($lno) ne $path} {
+		if {$file ne $path} {
 			$w_cmit insert end "Original File:\t" header_key
-			$w_cmit insert end "[escape_path $line_file($lno)]\n" header_val
+			$w_cmit insert end "[escape_path $file]\n" header_val
 		}
 
 		$w_cmit insert end "\n$msg"
 method _copycommit {} {
 	set pos @$::cursorX,$::cursorY
 	set lno [lindex [split [$::cursorW index $pos] .] 0]
-	if {![catch {set commit $line_commit($lno)}]} {
+	set dat [lindex $line_data $lno]
+	if {$dat ne {}} {
 		clipboard clear
 		clipboard append \
 			-format STRING \
 			-type STRING \
-			-- $commit
+			-- [lindex $dat 0]
 	}
 }
 
 method _show_tooltip {cur_w pos} {
 	set lno [lindex [split [$cur_w index $pos] .] 0]
-	if {[catch {set cmit $line_commit($lno)}]} {
+	set dat [lindex $line_data $lno]
+	if {$dat eq {}} {
 		_hide_tooltip $this
 		return
 	}
+	set cmit [lindex $dat 0]
 
 	if {$cmit eq $highlight_commit} {
 		_hide_tooltip $this
 		[expr {$pos_x - [winfo rootx $cur_w]}] \
 		[expr {$pos_y - [winfo rooty $cur_w]}]] ,]
 	set lno [lindex [split [$cur_w index $pos] .] 0]
-	set cmit $line_commit($lno)
+	set dat [lindex $line_data $lno]
+	set cmit [lindex $dat 0]
+	set file [lindex $dat 1]
 
 	set author_name {}
 	set author_email {}
 $author_name $author_email  $author_time
 $summary"
 
-	set file $line_file($lno)
 	if {$file ne $path} {
 		append tooltip_text "
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.