X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=lib%2Fblame.tcl;h=33e8489d067c4955f72075fe1de22482f0463e30;hb=22c6769d917635732fe9e6824aa30536155266b3;hp=eaf2fb36f8891ec510a6eefe5249cb2d69575bd2;hpb=8154e1a624cd768aada2565857151e6032b42a8c;p=git.git diff --git a/lib/blame.tcl b/lib/blame.tcl index eaf2fb36f..33e8489d0 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -3,17 +3,24 @@ class blame { -field commit ; # input commit to blame -field path ; # input filename to view in $commit - -field w -field w_line -field w_cgrp -field w_load -field w_file -field w_cmit -field status - +image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} + +field commit ; # input commit to blame +field path ; # input filename to view in $commit +field history {}; # viewer history: {commit path} + +field w ; # top window in this viewer +field w_back ; # our back button +field w_path ; # label showing the current file path +field w_line ; # text column: all line numbers +field w_cgrp ; # text column: abbreviated commit SHA-1s +field w_load ; # text column: loaded indicator +field w_file ; # text column: actual file data +field w_cmit ; # pane showing commit message +field status ; # text variable bound to status bar +field old_height ; # last known height of $w.file_pane + +field current_fd {} ; # background process running field highlight_line -1 ; # current line selected field highlight_commit {} ; # sha1 of commit selected @@ -51,18 +58,58 @@ constructor new {i_commit i_path} { make_toplevel top w wm title $top "[appname] ([reponame]): File Viewer" - set status "Loading $commit:$path..." - label $w.path -text "$commit:$path" \ + frame $w.header -background orange + label $w.header.commit_l \ + -text {Commit:} \ + -background orange \ -anchor w \ - -justify left \ - -borderwidth 1 \ - -relief sunken \ - -font font_uibold - pack $w.path -side top -fill x - - frame $w.out - set w_load $w.out.loaded_t + -justify left + set w_back $w.header.commit_b + button $w_back \ + -command [cb _history_menu] \ + -image ::blame::img_back_arrow \ + -borderwidth 0 \ + -relief flat \ + -state disabled \ + -background orange \ + -activebackground orange + label $w.header.commit \ + -textvariable @commit \ + -background orange \ + -anchor w \ + -justify left + label $w.header.path_l \ + -text {File:} \ + -background orange \ + -anchor w \ + -justify left + set w_path $w.header.path + label $w_path \ + -background orange \ + -anchor w \ + -justify left + pack $w.header.commit_l -side left + pack $w_back -side left + pack $w.header.commit -side left + pack $w_path -fill x -side right + pack $w.header.path_l -side right + + panedwindow $w.file_pane -orient vertical + frame $w.file_pane.out + frame $w.file_pane.cm + $w.file_pane add $w.file_pane.out \ + -sticky nsew \ + -minsize 100 \ + -height 100 \ + -width 100 + $w.file_pane add $w.file_pane.cm \ + -sticky nsew \ + -minsize 25 \ + -height 25 \ + -width 100 + + set w_load $w.file_pane.out.loaded_t text $w_load \ -background white -borderwidth 0 \ -state disabled \ @@ -72,7 +119,7 @@ constructor new {i_commit i_path} { -font font_diff $w_load tag conf annotated -background grey - set w_line $w.out.linenumber_t + set w_line $w.file_pane.out.linenumber_t text $w_line \ -background white -borderwidth 0 \ -state disabled \ @@ -82,7 +129,7 @@ constructor new {i_commit i_path} { -font font_diff $w_line tag conf linenumber -justify right - set w_cgrp $w.out.commit_t + set w_cgrp $w.file_pane.out.commit_t text $w_cgrp \ -background white -borderwidth 0 \ -state disabled \ @@ -90,19 +137,29 @@ constructor new {i_commit i_path} { -height 40 \ -width 4 \ -font font_diff - - set w_file $w.out.file_t + $w_cgrp tag conf curr_commit + $w_cgrp tag conf prior_commit \ + -foreground blue \ + -underline 1 + $w_cgrp tag bind prior_commit \ + \ + "[cb _load_commit @%x,%y];break" + + set w_file $w.file_pane.out.file_t text $w_file \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 80 \ - -xscrollcommand [list $w.out.sbx set] \ + -xscrollcommand [list $w.file_pane.out.sbx set] \ -font font_diff - scrollbar $w.out.sbx -orient h -command [list $w_file xview] - scrollbar $w.out.sby -orient v \ + scrollbar $w.file_pane.out.sbx \ + -orient h \ + -command [list $w_file xview] + scrollbar $w.file_pane.out.sby \ + -orient v \ -command [list scrollbar2many [list \ $w_load \ $w_line \ @@ -114,31 +171,21 @@ constructor new {i_commit i_path} { $w_line \ $w_load \ $w_file \ - $w.out.sby \ + $w.file_pane.out.sby \ -sticky nsew - grid conf $w.out.sbx -column 3 -sticky we - grid columnconfigure $w.out 3 -weight 1 - grid rowconfigure $w.out 0 -weight 1 - pack $w.out -fill both -expand 1 + grid conf $w.file_pane.out.sbx -column 3 -sticky we + grid columnconfigure $w.file_pane.out 3 -weight 1 + grid rowconfigure $w.file_pane.out 0 -weight 1 - label $w.status \ - -textvariable @status \ - -anchor w \ - -justify left \ - -borderwidth 1 \ - -relief sunken - pack $w.status -side bottom -fill x - - frame $w.cm - set w_cmit $w.cm.t + set w_cmit $w.file_pane.cm.t text $w_cmit \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 10 \ -width 80 \ - -xscrollcommand [list $w.cm.sbx set] \ - -yscrollcommand [list $w.cm.sby set] \ + -xscrollcommand [list $w.file_pane.cm.sbx set] \ + -yscrollcommand [list $w.file_pane.cm.sby set] \ -font font_diff $w_cmit tag conf header_key \ -tabs {3c} \ @@ -148,12 +195,24 @@ constructor new {i_commit i_path} { -background $active_color \ -font font_ui $w_cmit tag raise sel - scrollbar $w.cm.sbx -orient h -command [list $w_cmit xview] - scrollbar $w.cm.sby -orient v -command [list $w_cmit yview] - pack $w.cm.sby -side right -fill y - pack $w.cm.sbx -side bottom -fill x + scrollbar $w.file_pane.cm.sbx \ + -orient h \ + -command [list $w_cmit xview] + scrollbar $w.file_pane.cm.sby \ + -orient v \ + -command [list $w_cmit yview] + pack $w.file_pane.cm.sby -side right -fill y + pack $w.file_pane.cm.sbx -side bottom -fill x pack $w_cmit -expand 1 -fill both - pack $w.cm -side bottom -fill x + + frame $w.status \ + -borderwidth 1 \ + -relief sunken + label $w.status.l \ + -textvariable @status \ + -anchor w \ + -justify left + pack $w.status.l -side left menu $w.ctxm -tearoff 0 $w.ctxm add command \ @@ -172,8 +231,8 @@ constructor new {i_commit i_path} { $w_load \ $w_line \ $w_file \ - ] yview $w.out.sby] - bind $i " + ] yview $w.file_pane.out.sby] + bind $i " [cb _hide_tooltip] [cb _click $i @%x,%y] focus $i @@ -210,8 +269,91 @@ constructor new {i_commit i_path} { bind $w_cmit [list focus $w_cmit] bind $top [list focus $top] - bind $top [list delete_this $this] + bind $w_file [list delete_this $this] + + grid configure $w.header -sticky ew + grid configure $w.file_pane -sticky nsew + grid configure $w.status -sticky ew + grid columnconfigure $top 0 -weight 1 + grid rowconfigure $top 0 -weight 0 + grid rowconfigure $top 1 -weight 1 + grid rowconfigure $top 2 -weight 0 + + set req_w [winfo reqwidth $top] + set req_h [winfo reqheight $top] + if {$req_w < 600} {set req_w 600} + if {$req_h < 400} {set req_h 400} + set g "${req_w}x${req_h}" + wm geometry $top $g + update + + set old_height [winfo height $w.file_pane] + $w.file_pane sash place 0 \ + [lindex [$w.file_pane sash coord 0] 0] \ + [expr {int($old_height * 0.70)}] + bind $w.file_pane \ + "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" + + _load $this +} + +method _load {} { + _hide_tooltip $this + + if {$total_lines != 0 || $current_fd ne {}} { + if {$current_fd ne {}} { + catch {close $current_fd} + set current_fd {} + } + set highlight_line -1 + set highlight_commit {} + set total_lines 0 + set blame_lines 0 + set commit_count 0 + set commit_list {} + array unset order + array unset line_commit + array unset line_file + + $w_load conf -state normal + $w_cgrp conf -state normal + $w_line conf -state normal + $w_file conf -state normal + + $w_load delete 0.0 end + $w_cgrp delete 0.0 end + $w_line delete 0.0 end + $w_file delete 0.0 end + + $w_load conf -state disabled + $w_cgrp conf -state disabled + $w_line conf -state disabled + $w_file conf -state disabled + } + + if {[winfo exists $w.status.c]} { + $w.status.c coords bar 0 0 0 20 + } else { + canvas $w.status.c \ + -width 100 \ + -height [expr {int([winfo reqheight $w.status.l] * 0.6)}] \ + -borderwidth 1 \ + -relief groove \ + -highlightt 0 + $w.status.c create rectangle 0 0 0 20 -tags bar -fill navy + pack $w.status.c -side right + } + + if {$history eq {}} { + $w_back conf -state disabled + } else { + $w_back conf -state normal + } + lappend history [list $commit $path] + + set status "Loading $commit:[escape_path $path]..." + $w_path conf -text [escape_path $path] if {$commit eq {}} { set fd [open $path r] } else { @@ -220,9 +362,52 @@ constructor new {i_commit i_path} { } fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [cb _read_file $fd] + set current_fd $fd +} + +method _history_menu {} { + set m $w.backmenu + if {[winfo exists $m]} { + $m delete 0 end + } else { + menu $m -tearoff 0 + } + + for {set i [expr {[llength $history] - 2}] + } {$i >= 0} {incr i -1} { + set e [lindex $history $i] + set c [lindex $e 0] + set f [lindex $e 1] + + if {[regexp {^[0-9a-f]{40}$} $c]} { + set t [string range $c 0 8]... + } else { + set t $c + } + if {![catch {set summary $header($c,summary)}]} { + append t " $summary" + } + + $m add command -label $t -command [cb _goback $i $c $f] + } + 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} { + set history [lrange $history 0 [expr {$i - 1}]] + set commit $c + set path $f + _load $this } method _read_file {fd} { + if {$fd ne $current_fd} { + catch {close $fd} + return + } + $w_load conf -state normal $w_cgrp conf -state normal $w_line conf -state normal @@ -249,7 +434,7 @@ method _read_file {fd} { if {[eof $fd]} { close $fd _status $this - set cmd [list git blame -M -C --incremental] + set cmd {nice git blame -M -C --incremental} if {$commit eq {}} { lappend cmd --contents $path } else { @@ -259,12 +444,18 @@ method _read_file {fd} { set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [cb _read_blame $fd] + set current_fd $fd } } ifdeleted { catch {close $fd} } method _read_blame {fd} { variable group_colors + if {$fd ne $current_fd} { + catch {close $fd} + return + } + $w_cgrp conf -state normal while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ @@ -294,16 +485,44 @@ method _read_blame {fd} { set cmit $r_commit if {[regexp {^0{40}$} $cmit]} { - set abbr work + set commit_abbr work + set commit_type curr_commit + } elseif {$cmit eq $commit} { + set commit_abbr this + set commit_type curr_commit } else { - set abbr [string range $cmit 0 4] + set commit_type prior_commit + set commit_abbr [string range $cmit 0 4] } - if {![catch {set ncmit $line_commit([expr {$lno - 1}])}]} { - if {$ncmit eq $cmit} { - set abbr | + set author_abbr {} + set a_name {} + catch {set a_name $header($cmit,author)} + while {$a_name ne {}} { + if {![regexp {^([[:upper:]])} $a_name _a]} break + append author_abbr $_a + unset _a + if {![regsub \ + {^[[:upper:]][^\s]*\s+} \ + $a_name {} a_name ]} break + } + if {$author_abbr eq {}} { + set author_abbr { |} + } else { + set author_abbr [string range $author_abbr 0 3] + while {[string length $author_abbr] < 4} { + set author_abbr " $author_abbr" } } + unset a_name + + set first_lno $lno + while { + ![catch {set ncmit $line_commit([expr {$first_lno - 1}])}] + && $ncmit eq $cmit + } { + incr first_lno -1 + } while {$n > 0} { set lno_e "$lno.0 lineend + 1c" @@ -323,8 +542,13 @@ method _read_blame {fd} { set line_file($lno) $file $w_cgrp delete $lno.0 "$lno.0 lineend" - $w_cgrp insert $lno.0 $abbr - set abbr | + if {$lno == $first_lno} { + $w_cgrp insert $lno.0 $commit_abbr $commit_type + } elseif {$lno == [expr {$first_lno + 1}]} { + $w_cgrp insert $lno.0 $author_abbr + } else { + $w_cgrp insert $lno.0 { |} + } $w_cgrp tag add g$cmit $lno.0 $lno_e $w_line tag add g$cmit $lno.0 $lno_e @@ -348,12 +572,20 @@ method _read_blame {fd} { incr blame_lines } - if {![catch {set ncmit $line_commit($lno)}]} { - if {$ncmit eq $cmit} { - $w_cgrp delete $lno.0 "$lno.0 lineend + 1c" - $w_cgrp insert $lno.0 "|\n" + while {![catch {set ncmit $line_commit($lno)}] + && $ncmit eq $cmit} { + $w_cgrp delete $lno.0 "$lno.0 lineend" + + if {$lno == $first_lno} { + $w_cgrp insert $lno.0 $commit_abbr $commit_type + } elseif {$lno == [expr {$first_lno + 1}]} { + $w_cgrp insert $lno.0 $author_abbr + } else { + $w_cgrp insert $lno.0 { |} } + incr lno } + } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} { set header($r_commit,$key) $data } @@ -362,7 +594,9 @@ method _read_blame {fd} { if {[eof $fd]} { close $fd + set current_fd {} set status {Annotation complete.} + destroy $w.status.c } else { _status $this } @@ -377,6 +611,7 @@ method _status {} { set status [format \ "Loading annotations... %i of %i lines annotated (%2i%%)" \ $have $total $pdone] + $w.status.c coords bar 0 0 $pdone 20 } method _click {cur_w pos} { @@ -385,6 +620,16 @@ method _click {cur_w pos} { _showcommit $this $lno } +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 +} + method _showcommit {lno} { global repo_config variable active_color @@ -597,4 +842,19 @@ method _hide_tooltip {} { } } +method _resize {new_height} { + set diff [expr {$new_height - $old_height}] + if {$diff == 0} return + + set my [expr {[winfo height $w.file_pane] - 25}] + set o [$w.file_pane sash coord 0] + set ox [lindex $o 0] + set oy [expr {[lindex $o 1] + $diff}] + if {$oy < 0} {set oy 0} + if {$oy > $my} {set oy $my} + $w.file_pane sash place 0 $ox $oy + + set old_height $new_height +} + }