Teach "git add -i" to colorize whitespace errors

Rather than replicating the colorization logic of "git diff-files" we
rely on "git diff-files" itself. This guarantees consistent colorization
in and outside "git add -i".

Seeing as speed is not a concern here (the bottleneck is how fast the
user can read, not how fast "git diff-files" runs) we do this by
actually running it twice, once without color and once with.

In this way as the whitespace colorization provided by "git diff-files"
evolves (per-path attributes, new classes of whitespace error), "git
add -i" will automatically benefit from it and stay in synch.

Also, by working with two sets of diff output (an uncolorized one for
internal processing and a colorized one for display only) we minimize
the risk of regressions because the changes required to implement this
are minimally invasive.

Signed-off-by: Wincent Colaiuta <win@wincent.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Wincent Colaiuta 2007-12-07 13:35:10 +01:00 committed by Junio C Hamano
parent 8e566f24b3
commit 4af756f31b
1 changed files with 34 additions and 39 deletions

View File

@ -525,42 +525,23 @@ sub add_untracked_cmd {
sub parse_diff { sub parse_diff {
my ($path) = @_; my ($path) = @_;
my @diff = run_cmd_pipe(qw(git diff-files -p --), $path); my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
my (@hunk) = { TEXT => [] }; my @colored = ();

if ($diff_use_color) {
for (@diff) { @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
if (/^@@ /) {
push @hunk, { TEXT => [] };
} }
push @{$hunk[-1]{TEXT}}, $_; my (@hunk) = { TEXT => [], DISPLAY => [] };

for (my $i = 0; $i < @diff; $i++) {
if ($diff[$i] =~ /^@@ /) {
push @hunk, { TEXT => [], DISPLAY => [] };
}
push @{$hunk[-1]{TEXT}}, $diff[$i];
push @{$hunk[-1]{DISPLAY}},
($diff_use_color ? $colored[$i] : $diff[$i]);
} }
return @hunk; return @hunk;
} }


sub colored_diff_hunk {
my ($text) = @_;
# return the text, so that it can be passed to print()
my @ret;
for (@$text) {
if (!$diff_use_color) {
push @ret, $_;
next;
}

if (/^\+/) {
push @ret, colored($new_color, $_);
} elsif (/^\-/) {
push @ret, colored($old_color, $_);
} elsif (/^\@/) {
push @ret, colored($fraginfo_color, $_);
} elsif (/^ /) {
push @ret, colored($normal_color, $_);
} else {
push @ret, colored($metainfo_color, $_);
}
}
return @ret;
}

sub hunk_splittable { sub hunk_splittable {
my ($text) = @_; my ($text) = @_;


@ -578,9 +559,11 @@ sub parse_hunk_header {
} }


sub split_hunk { sub split_hunk {
my ($text) = @_; my ($text, $display) = @_;
my @split = (); my @split = ();

if (!defined $display) {
$display = $text;
}
# If there are context lines in the middle of a hunk, # If there are context lines in the middle of a hunk,
# it can be split, but we would need to take care of # it can be split, but we would need to take care of
# overlaps later. # overlaps later.
@ -594,16 +577,19 @@ sub split_hunk {
my $i = $hunk_start - 1; my $i = $hunk_start - 1;
my $this = +{ my $this = +{
TEXT => [], TEXT => [],
DISPLAY => [],
OLD => $o_ofs, OLD => $o_ofs,
NEW => $n_ofs, NEW => $n_ofs,
OCNT => 0, OCNT => 0,
NCNT => 0, NCNT => 0,
ADDDEL => 0, ADDDEL => 0,
POSTCTX => 0, POSTCTX => 0,
USE => undef,
}; };


while (++$i < @$text) { while (++$i < @$text) {
my $line = $text->[$i]; my $line = $text->[$i];
my $display = $display->[$i];
if ($line =~ /^ /) { if ($line =~ /^ /) {
if ($this->{ADDDEL} && if ($this->{ADDDEL} &&
!defined $next_hunk_start) { !defined $next_hunk_start) {
@ -615,6 +601,7 @@ sub split_hunk {
$next_hunk_start = $i; $next_hunk_start = $i;
} }
push @{$this->{TEXT}}, $line; push @{$this->{TEXT}}, $line;
push @{$this->{DISPLAY}}, $display;
$this->{OCNT}++; $this->{OCNT}++;
$this->{NCNT}++; $this->{NCNT}++;
if (defined $next_hunk_start) { if (defined $next_hunk_start) {
@ -637,6 +624,7 @@ sub split_hunk {
redo OUTER; redo OUTER;
} }
push @{$this->{TEXT}}, $line; push @{$this->{TEXT}}, $line;
push @{$this->{DISPLAY}}, $display;
$this->{ADDDEL}++; $this->{ADDDEL}++;
if ($line =~ /^-/) { if ($line =~ /^-/) {
$this->{OCNT}++; $this->{OCNT}++;
@ -661,9 +649,14 @@ sub split_hunk {
" +$n_ofs" . " +$n_ofs" .
(($n_cnt != 1) ? ",$n_cnt" : '') . (($n_cnt != 1) ? ",$n_cnt" : '') .
" @@\n"); " @@\n");
my $display_head = $head;
unshift @{$hunk->{TEXT}}, $head; unshift @{$hunk->{TEXT}}, $head;
if ($diff_use_color) {
$display_head = colored($fraginfo_color, $head);
} }
return map { $_->{TEXT} } @split; unshift @{$hunk->{DISPLAY}}, $display_head;
}
return @split;
} }


sub find_last_o_ctx { sub find_last_o_ctx {
@ -794,7 +787,9 @@ sub patch_update_file {
my ($ix, $num); my ($ix, $num);
my $path = shift; my $path = shift;
my ($head, @hunk) = parse_diff($path); my ($head, @hunk) = parse_diff($path);
print colored_diff_hunk($head->{TEXT}); for (@{$head->{DISPLAY}}) {
print;
}
$num = scalar @hunk; $num = scalar @hunk;
$ix = 0; $ix = 0;


@ -836,7 +831,9 @@ sub patch_update_file {
if (hunk_splittable($hunk[$ix]{TEXT})) { if (hunk_splittable($hunk[$ix]{TEXT})) {
$other .= '/s'; $other .= '/s';
} }
print colored_diff_hunk($hunk[$ix]{TEXT}); for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? "; print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
my $line = <STDIN>; my $line = <STDIN>;
if ($line) { if ($line) {
@ -889,14 +886,12 @@ sub patch_update_file {
next; next;
} }
elsif ($other =~ /s/ && $line =~ /^s/) { elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}); my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
if (1 < @split) { if (1 < @split) {
print colored $header_color, "Split into ", print colored $header_color, "Split into ",
scalar(@split), " hunks.\n"; scalar(@split), " hunks.\n";
} }
splice(@hunk, $ix, 1, splice (@hunk, $ix, 1, @split);
map { +{ TEXT => $_, USE => undef } }
@split);
$num = scalar @hunk; $num = scalar @hunk;
next; next;
} }