@@ -2249,6 +2249,7 @@ proc makewindow {} {
22492249 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
22502250 global ctxbut
22512251 bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2252+ bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
22522253
22532254 set maincursor [. cget -cursor]
22542255 set textcursor [$ctext cget -cursor]
@@ -2291,6 +2292,13 @@ proc makewindow {} {
22912292 {mc " Blame parent commit" command {external_blame 1}}
22922293 }
22932294 $flist_menu configure -tearoff 0
2295+
2296+ global diff_menu
2297+ set diff_menu .diffctxmenu
2298+ makemenu $diff_menu {
2299+ {mc " Run git gui blame on this line" command {external_blame_diff}}
2300+ }
2301+ $diff_menu configure -tearoff 0
22942302}
22952303
22962304# Windows sends all mouse wheel events to the current focused window, not
@@ -2993,6 +3001,34 @@ proc pop_flist_menu {w X Y x y} {
29933001 tk_popup $flist_menu $X $Y
29943002}
29953003
3004+ proc find_ctext_fileinfo {line} {
3005+ global ctext_file_names ctext_file_lines
3006+
3007+ set ok [bsearch $ctext_file_lines $line ]
3008+ set tline [lindex $ctext_file_lines $ok ]
3009+
3010+ if {$ok >= [llength $ctext_file_lines ] || $line < $tline } {
3011+ return {}
3012+ } else {
3013+ return [list [lindex $ctext_file_names $ok ] $tline ]
3014+ }
3015+ }
3016+
3017+ proc pop_diff_menu {w X Y x y} {
3018+ global ctext diff_menu flist_menu_file
3019+ global diff_menu_txtpos diff_menu_line
3020+ global diff_menu_filebase
3021+
3022+ stopfinding
3023+ set diff_menu_txtpos [split [$w index " @$x ,$y " ] " ." ]
3024+ set diff_menu_line [lindex $diff_menu_txtpos 0]
3025+ set f [find_ctext_fileinfo $diff_menu_line ]
3026+ if {$f eq {}} return
3027+ set flist_menu_file [lindex $f 0]
3028+ set diff_menu_filebase [lindex $f 1]
3029+ tk_popup $diff_menu $X $Y
3030+ }
3031+
29963032proc flist_hl {only} {
29973033 global flist_menu_file findstring gdttype
29983034
@@ -3099,7 +3135,96 @@ proc external_diff {} {
30993135 }
31003136}
31013137
3102- proc external_blame {parent_idx} {
3138+ proc find_hunk_blamespec {base line} {
3139+ global ctext
3140+
3141+ # Find and parse the hunk header
3142+ set s_lix [$ctext search -backwards -regexp ^@@ " $line .0 lineend" $base .0]
3143+ if {$s_lix eq {}} return
3144+
3145+ set s_line [$ctext get $s_lix " $s_lix + 1 lines" ]
3146+ if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
3147+ s_line old_specs osz osz1 new_line nsz]} {
3148+ return
3149+ }
3150+
3151+ # base lines for the parents
3152+ set base_lines [list $new_line ]
3153+ foreach old_spec [lrange [split $old_specs " " ] 1 end] {
3154+ if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
3155+ old_spec old_line osz]} {
3156+ return
3157+ }
3158+ lappend base_lines $old_line
3159+ }
3160+
3161+ # Now scan the lines to determine offset within the hunk
3162+ set parent {}
3163+ set max_parent [expr {[llength $base_lines ]-2}]
3164+ set dline 0
3165+ set s_lno [lindex [split $s_lix " ." ] 0]
3166+
3167+ for {set i $line } {$i > $s_lno } {incr i -1} {
3168+ set c_line [$ctext get $i .0 " $i .0 + 1 lines" ]
3169+ # Determine if the line is removed
3170+ set chunk [string range $c_line 0 $max_parent ]
3171+ set removed_idx [string first " -" $chunk ]
3172+ # Choose a parent index
3173+ if {$parent eq {}} {
3174+ if {$removed_idx >= 0} {
3175+ set parent $removed_idx
3176+ } else {
3177+ set unchanged_idx [string first " " $chunk ]
3178+ if {$unchanged_idx >= 0} {
3179+ set parent $unchanged_idx
3180+ } else {
3181+ # blame the current commit
3182+ set parent -1
3183+ }
3184+ }
3185+ }
3186+ # then count other lines that belong to it
3187+ if {$parent >= 0} {
3188+ set code [string index $c_line $parent ]
3189+ if {$code eq " -" || ($removed_idx < 0 && $code ne " +" )} {
3190+ incr dline
3191+ }
3192+ } else {
3193+ if {$removed_idx < 0} {
3194+ incr dline
3195+ }
3196+ }
3197+ }
3198+
3199+ if {$parent eq {}} { set parent -1 }
3200+ incr parent
3201+ incr dline [lindex $base_lines $parent ]
3202+ return [list $parent $dline ]
3203+ }
3204+
3205+ proc external_blame_diff {} {
3206+ global currentid diffmergeid cmitmode
3207+ global diff_menu_txtpos diff_menu_line
3208+ global diff_menu_filebase flist_menu_file
3209+
3210+ if {$cmitmode eq " tree" } {
3211+ set parent_idx 0
3212+ set line [expr {$diff_menu_line - $diff_menu_filebase - 1}]
3213+ } else {
3214+ set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line ]
3215+ if {$hinfo ne {}} {
3216+ set parent_idx [lindex $hinfo 0]
3217+ set line [lindex $hinfo 1]
3218+ } else {
3219+ set parent_idx 0
3220+ set line 0
3221+ }
3222+ }
3223+
3224+ external_blame $parent_idx $line
3225+ }
3226+
3227+ proc external_blame {parent_idx {line {}}} {
31033228 global flist_menu_file
31043229 global nullid nullid2
31053230 global parentlist selectedline currentid
@@ -3115,7 +3240,12 @@ proc external_blame {parent_idx} {
31153240 return
31163241 }
31173242
3118- if {[catch {exec git gui blame $base_commit $flist_menu_file &} err]} {
3243+ set cmdline [list git gui blame]
3244+ if {$line ne {} && $line > 1} {
3245+ lappend cmdline " --line=$line "
3246+ }
3247+ lappend cmdline $base_commit $flist_menu_file
3248+ if {[catch {eval exec $cmdline &} err]} {
31193249 error_popup " [ mc " git gui blame: command failed:" ] $err "
31203250 }
31213251}
@@ -6364,6 +6494,7 @@ proc gettreeline {gtf id} {
63646494
63656495proc showfile {f} {
63666496 global treefilelist treeidlist diffids nullid nullid2
6497+ global ctext_file_names ctext_file_lines
63676498 global ctext commentend
63686499
63696500 set i [lsearch -exact $treefilelist($diffids) $f ]
@@ -6387,6 +6518,8 @@ proc showfile {f} {
63876518 filerun $bf [list getblobline $bf $diffids ]
63886519 $ctext config -state normal
63896520 clear_ctext $commentend
6521+ lappend ctext_file_names $f
6522+ lappend ctext_file_lines [lindex [split $commentend " ." ] 0]
63906523 $ctext insert end " \n "
63916524 $ctext insert end " $f \n " filesep
63926525 $ctext config -state disabled
@@ -6447,6 +6580,7 @@ proc mergediff {id} {
64476580proc getmergediffline {mdf id np} {
64486581 global diffmergeid ctext cflist mergemax
64496582 global difffilestart mdifffd treediffs
6583+ global ctext_file_names ctext_file_lines
64506584 global diffencoding
64516585
64526586 $ctext conf -state normal
@@ -6465,6 +6599,8 @@ proc getmergediffline {mdf id np} {
64656599 lappend difffilestart $here
64666600 lappend treediffs($id ) $fname
64676601 add_flist [list $fname ]
6602+ lappend ctext_file_names $fname
6603+ lappend ctext_file_lines [lindex [split $here " ." ] 0]
64686604 set diffencoding [get_path_encoding $fname ]
64696605 set l [expr {(78 - [string length $fname ]) / 2}]
64706606 set pad [string range " ----------------------------------------" 1 $l ]
@@ -6733,11 +6869,13 @@ proc setinlist {var i val} {
67336869
67346870proc makediffhdr {fname ids} {
67356871 global ctext curdiffstart treediffs
6872+ global ctext_file_names
67366873
67376874 set i [lsearch -exact $treediffs($ids) $fname ]
67386875 if {$i >= 0} {
67396876 setinlist difffilestart $i $curdiffstart
67406877 }
6878+ set ctext_file_names [lreplace $ctext_file_names end end $fname ]
67416879 set l [expr {(78 - [string length $fname ]) / 2}]
67426880 set pad [string range " ----------------------------------------" 1 $l ]
67436881 $ctext insert $curdiffstart " $pad $fname $pad " filesep
@@ -6746,6 +6884,7 @@ proc makediffhdr {fname ids} {
67466884proc getblobdiffline {bdf ids} {
67476885 global diffids blobdifffd ctext curdiffstart
67486886 global diffnexthead diffnextnote difffilestart
6887+ global ctext_file_names ctext_file_lines
67496888 global diffinhdr treediffs
67506889 global diffencoding
67516890
@@ -6763,6 +6902,8 @@ proc getblobdiffline {bdf ids} {
67636902 # start of a new file
67646903 $ctext insert end " \n "
67656904 set curdiffstart [$ctext index " end - 1c" ]
6905+ lappend ctext_file_names " "
6906+ lappend ctext_file_lines [lindex [split $curdiffstart " ." ] 0]
67666907 $ctext insert end " \n " filesep
67676908 # If the name hasn't changed the length will be odd,
67686909 # the middle char will be a space, and the two bits either
@@ -6899,6 +7040,7 @@ proc nextfile {} {
68997040
69007041proc clear_ctext {{first 1.0}} {
69017042 global ctext smarktop smarkbot
7043+ global ctext_file_names ctext_file_lines
69027044 global pendinglinks
69037045
69047046 set l [lindex [split $first .] 0]
@@ -6912,6 +7054,8 @@ proc clear_ctext {{first 1.0}} {
69127054 if {$first eq " 1.0" } {
69137055 catch {unset pendinglinks}
69147056 }
7057+ set ctext_file_names {}
7058+ set ctext_file_lines {}
69157059}
69167060
69177061proc settabs {{firstab {}}} {
0 commit comments