git-gui: Reimplement and enhance auto-selection of diffs.

Generalize the next_diff system, and implement auto-reselection
for merge tool resolution and reshow_diff. Also add auto-selection
of diffs after rescan, if no diff is already selected.

New auto-select rules:

- Rescan auto-selects the first conflicting file, or if none
  a modified tracked file, if nothing was selected previously.
- Resolving a conflict auto-selects the nearest conflicting
  file, or nothing if everything is resolved.
- Staging the last remaining hunk auto-selects the nearest
  modified staged file.
- Staging a file through its icon auto-selects the nearest file.

Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Alexander Gavrilov 2008-08-31 01:02:56 +04:00 committed by Shawn O. Pearce
parent ff515d81fa
commit 29853b9010
3 changed files with 109 additions and 39 deletions

View File

@ -1326,6 +1326,8 @@ proc rescan_done {fd buf after} {
unlock_index unlock_index
display_all_files display_all_files
if {$current_diff_path ne {}} reshow_diff if {$current_diff_path ne {}} reshow_diff
if {$current_diff_path eq {}} select_first_diff
uplevel #0 $after uplevel #0 $after
} }
@ -1821,7 +1823,98 @@ proc do_commit {} {
proc next_diff {} { proc next_diff {} {
global next_diff_p next_diff_w next_diff_i global next_diff_p next_diff_w next_diff_i
show_diff $next_diff_p $next_diff_w $next_diff_i show_diff $next_diff_p $next_diff_w {}
}
proc find_anchor_pos {lst name} {
set lid [lsearch -sorted -exact $lst $name]
if {$lid == -1} {
set lid 0
foreach lname $lst {
if {$lname >= $name} break
incr lid
}
}
return $lid
}
proc find_file_from {flist idx delta path mmask} {
global file_states
set len [llength $flist]
while {$idx >= 0 && $idx < $len} {
set name [lindex $flist $idx]
if {$name ne $path && [info exists file_states($name)]} {
set state [lindex $file_states($name) 0]
if {$mmask eq {} || [regexp $mmask $state]} {
return $idx
}
}
incr idx $delta
}
return {}
}
proc find_next_diff {w path {lno {}} {mmask {}}} {
global next_diff_p next_diff_w next_diff_i
global file_lists ui_index ui_workdir
set flist $file_lists($w)
if {$lno eq {}} {
set lno [find_anchor_pos $flist $path]
} else {
incr lno -1
}
if {$mmask ne {} && ![regexp {(^\^)|(\$$)} $mmask]} {
if {$w eq $ui_index} {
set mmask "^$mmask"
} else {
set mmask "$mmask\$"
}
}
set idx [find_file_from $flist $lno 1 $path $mmask]
if {$idx eq {}} {
incr lno -1
set idx [find_file_from $flist $lno -1 $path $mmask]
}
if {$idx ne {}} {
set next_diff_w $w
set next_diff_p [lindex $flist $idx]
set next_diff_i [expr {$idx+1}]
return 1
} else {
return 0
}
}
proc next_diff_after_action {w path {lno {}} {mmask {}}} {
global current_diff_path
if {$path ne $current_diff_path} {
return {}
} elseif {[find_next_diff $w $path $lno $mmask]} {
return {next_diff;}
} else {
return {reshow_diff;}
}
}
proc select_first_diff {} {
global ui_workdir
if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
[find_next_diff $ui_workdir {} 1 {[^O]$}]} {
next_diff
}
} }
proc toggle_or_diff {w x y} { proc toggle_or_diff {w x y} {
@ -1851,32 +1944,7 @@ proc toggle_or_diff {w x y} {
} }
if {$col == 0 && $y > 1} { if {$col == 0 && $y > 1} {
set i [expr {$lno-1}] set after [next_diff_after_action $w $path $lno]
set ll [expr {[llength $file_lists($w)]-1}]
if {$i == $ll && $i == 0} {
set after {reshow_diff;}
} else {
global next_diff_p next_diff_w next_diff_i
set next_diff_w $w
if {$i < $ll} {
set i [expr {$i + 1}]
set next_diff_i $i
} else {
set next_diff_i $i
set i [expr {$i - 1}]
}
set next_diff_p [lindex $file_lists($w) $i]
if {$next_diff_p ne {} && $current_diff_path ne {}} {
set after {next_diff;}
} else {
set after {}
}
}
if {$w eq $ui_index} { if {$w eq $ui_index} {
update_indexinfo \ update_indexinfo \

View File

@ -24,10 +24,16 @@ proc reshow_diff {} {
set p $current_diff_path set p $current_diff_path
if {$p eq {}} { if {$p eq {}} {
# No diff is being shown. # No diff is being shown.
} elseif {$current_diff_side eq {} } elseif {$current_diff_side eq {}} {
|| [catch {set s $file_states($p)}]
|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
clear_diff clear_diff
} elseif {[catch {set s $file_states($p)}]
|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
if {[find_next_diff $current_diff_side $p {} {[^O]}]} {
next_diff
} else {
clear_diff
}
} else { } else {
set save_pos [lindex [$ui_diff yview] 0] set save_pos [lindex [$ui_diff yview] 0]
show_diff $p $current_diff_side {} $save_pos show_diff $p $current_diff_side {} $save_pos
@ -71,6 +77,7 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {
} }
if {$lno >= 1} { if {$lno >= 1} {
$w tag add in_diff $lno.0 [expr {$lno + 1}].0 $w tag add in_diff $lno.0 [expr {$lno + 1}].0
$w see $lno.0
} }
set s $file_states($path) set s $file_states($path)
@ -366,10 +373,9 @@ proc apply_hunk {x y} {
} }
unlock_index unlock_index
display_file $current_diff_path $mi display_file $current_diff_path $mi
# This should trigger shift to the next changed file
if {$o eq {_}} { if {$o eq {_}} {
clear_diff reshow_diff
} else {
set current_diff_path $current_diff_path
} }
} }

View File

@ -24,13 +24,9 @@ This operation can be undone only by restarting the merge." \
} }
proc merge_add_resolution {path} { proc merge_add_resolution {path} {
global current_diff_path global current_diff_path ui_workdir
if {$path eq $current_diff_path} { set after [next_diff_after_action $ui_workdir $path {} {^_?U}]
set after {reshow_diff;}
} else {
set after {}
}
update_index \ update_index \
[mc "Adding resolution for %s" [short_path $path]] \ [mc "Adding resolution for %s" [short_path $path]] \