Anonymous avatar Anonymous committed 0dfed77

git-gui: Jump to original line in blame viewer

When the user clicks on a commit link within one of the columns
in the blame viewer we now jump them not just to that commit/file
pair but also to the line of the original file. This saves the
user a lot of time, as they don't need to search through the new
file data for the chunk they were previously looking at.

We also restore the prior view when the user clicks the back button
to return to a pior commit/file pair that they were looking at.

Turned out this was quite tricky to get working in Tk. Every time
I tried to jump the text widgets to the correct locations by way
of the "yview moveto" or "see" subcommands Tk performed the change
until the current event finished dispatching, and then reset the
views back to 0, making the change never take place. Forcing Tk
to run the pending events before we jump the UI resolves the issue.

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

Comments (0)

Files changed (1)

 	bind $w.file_pane <Configure> \
 	"if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
-	_load $this
+	_load $this {}
-method _load {} {
+method _load {jump} {
 	_hide_tooltip $this
 	if {$total_lines != 0 || $current_fd ne {}} {
 			$i conf -state disabled
+		$w_cviewer conf -state normal
+		$w_cviewer delete 0.0 end
+		$w_cviewer conf -state disabled
 		set highlight_line -1
 		set highlight_column {}
 		set highlight_commit {}
 	} else {
 		$w_back conf -state normal
-	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
 		set fd [open "| $cmd" r]
 	fconfigure $fd -blocking 0 -translation lf -encoding binary
-	fileevent $fd readable [cb _read_file $fd]
+	fileevent $fd readable [cb _read_file $fd $jump]
 	set current_fd $fd
 		menu $m -tearoff 0
-	for {set i [expr {[llength $history] - 2}]
+	for {set i [expr {[llength $history] - 1}]
 		} {$i >= 0} {incr i -1} {
 		set e [lindex $history $i]
 		set c [lindex $e 0]
-		$m add command -label $t -command [cb _goback $i $c $f]
+		$m add command -label $t -command [cb _goback $i]
 	set X [winfo rootx $w_back]
 	set Y [expr {[winfo rooty $w_back] + [winfo height $w_back]}]
 	tk_popup $m $X $Y
-method _goback {i c f} {
+method _goback {i} {
+	set dat [lindex $history $i]
 	set history [lrange $history 0 [expr {$i - 1}]]
-	set commit $c
-	set path $f
-	_load $this
+	set commit [lindex $dat 0]
+	set path [lindex $dat 1]
+	_load $this [lrange $dat 2 5]
-method _read_file {fd} {
+method _read_file {fd jump} {
 	if {$fd ne $current_fd} {
 		catch {close $fd}
 	if {[eof $fd]} {
 		close $fd
+		# If we don't force Tk to update the widgets *right now*
+		# none of our jump commands will cause a change in the UI.
+		#
+		update
+		if {[llength $jump] == 1} {
+			set highlight_line [lindex $jump 0]
+			$w_file see "$highlight_line.0"
+		} elseif {[llength $jump] == 4} {
+			set highlight_column [lindex $jump 0]
+			set highlight_line [lindex $jump 1]
+			$w_file xview moveto [lindex $jump 2]
+			$w_file yview moveto [lindex $jump 3]
+		}
 		_exec_blame $this $w_asim @asim_data [list] {}
 } ifdeleted { catch {close $fd} }
 			set file [string range $line 9 end]
 			set n    $r_line_count
 			set lno  $r_final_line
+			set oln  $r_orig_line
 			set cmit $r_commit
 			if {[regexp {^0{40}$} $cmit]} {
 						$i tag remove g$g $lno.0 $lno_e
-				lset line_data $lno [list $cmit $file]
+				lset line_data $lno [list $cmit $file $oln]
 				$cur_w delete $lno.0 "$lno.0 lineend"
 				if {$lno == $first_lno} {
 				incr n -1
 				incr lno
+				incr oln
 				incr blame_lines
 	set lno [lindex [split [$cur_w index $pos] .] 0]
 	set dat [lindex $line_data $lno]
 	if {$dat ne {}} {
+		lappend history [list \
+			$commit $path \
+			$highlight_column \
+			$highlight_line \
+			[lindex [$w_file xview] 0] \
+			[lindex [$w_file yview] 0] \
+			]
 		set commit [lindex $dat 0]
 		set path   [lindex $dat 1]
-		_load $this
+		_load $this [list [lindex $dat 2]]
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
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.