2430
}
2431
2432
# process patch (diff) line (not to be used for diff headers),
2433
-# returning HTML-formatted (but not wrapped) line
2433
+# returning HTML-formatted (but not wrapped) line.
2434
+# If the line is passed as a reference, it is treated as HTML and not
2435
+# esc_html()'ed.
2436
sub format_diff_line {
2437
my ($line, $diff_class, $from, $to) = @_;
2438
2437
- chomp $line;
2438
- $line = untabify($line);
2439
-
2440
- if ($from && $to && $line =~ m/^\@{2} /) {
2441
- $line = format_unidiff_chunk_header($line, $from, $to);
2442
- } elsif ($from && $to && $line =~ m/^\@{3}/) {
2443
- $line = format_cc_diff_chunk_header($line, $from, $to);
2439
+ if (ref($line)) {
2440
+ $line = $$line;
2441
} else {
2445
- $line = esc_html($line, -nbsp=>1);
2442
+ chomp $line;
2443
+ $line = untabify($line);
2444
+
2445
+ if ($from && $to && $line =~ m/^\@{2} /) {
2446
+ $line = format_unidiff_chunk_header($line, $from, $to);
2447
+ } elsif ($from && $to && $line =~ m/^\@{3}/) {
2448
+ $line = format_cc_diff_chunk_header($line, $from, $to);
2449
+ } else {
2450
+ $line = esc_html($line, -nbsp=>1);
2451
+ }
2452
}
2453
2454
my $diff_classes = "diff";
5061
print @$ctx, @$rem, @$add;
5062
}
5063
5064
+# Format removed and added line, mark changed part and HTML-format them.
5065
+# Implementation is based on contrib/diff-highlight
5066
+sub format_rem_add_lines_pair {
5067
+ my ($rem, $add) = @_;
5068
+
5069
+ # We need to untabify lines before split()'ing them;
5070
+ # otherwise offsets would be invalid.
5071
+ chomp $rem;
5072
+ chomp $add;
5073
+ $rem = untabify($rem);
5074
+ $add = untabify($add);
5075
+
5076
+ my @rem = split(//, $rem);
5077
+ my @add = split(//, $add);
5078
+ my ($esc_rem, $esc_add);
5079
+ # Ignore +/- character, thus $prefix_len is set to 1.
5080
+ my ($prefix_len, $suffix_len) = (1, 0);
5081
+ my ($prefix_has_nonspace, $suffix_has_nonspace);
5082
+
5083
+ my $shorter = (@rem < @add) ? @rem : @add;
5084
+ while ($prefix_len < $shorter) {
5085
+ last if ($rem[$prefix_len] ne $add[$prefix_len]);
5086
+
5087
+ $prefix_has_nonspace = 1 if ($rem[$prefix_len] !~ /\s/);
5088
+ $prefix_len++;
5089
+ }
5090
+
5091
+ while ($prefix_len + $suffix_len < $shorter) {
5092
+ last if ($rem[-1 - $suffix_len] ne $add[-1 - $suffix_len]);
5093
+
5094
+ $suffix_has_nonspace = 1 if ($rem[-1 - $suffix_len] !~ /\s/);
5095
+ $suffix_len++;
5096
+ }
5097
+
5098
+ # Mark lines that are different from each other, but have some common
5099
+ # part that isn't whitespace. If lines are completely different, don't
5100
+ # mark them because that would make output unreadable, especially if
5101
+ # diff consists of multiple lines.
5102
+ if ($prefix_has_nonspace || $suffix_has_nonspace) {
5103
+ $esc_rem = esc_html_hl_regions($rem, 'marked',
5104
+ [$prefix_len, @rem - $suffix_len], -nbsp=>1);
5105
+ $esc_add = esc_html_hl_regions($add, 'marked',
5106
+ [$prefix_len, @add - $suffix_len], -nbsp=>1);
5107
+ } else {
5108
+ $esc_rem = esc_html($rem, -nbsp=>1);
5109
+ $esc_add = esc_html($add, -nbsp=>1);
5110
+ }
5111
+
5112
+ return format_diff_line(\$esc_rem, 'rem'),
5113
+ format_diff_line(\$esc_add, 'add');
5114
+}
5115
+
5116
+# HTML-format diff context, removed and added lines.
5117
+sub format_ctx_rem_add_lines {
5118
+ my ($ctx, $rem, $add, $is_combined) = @_;
5119
+ my (@new_ctx, @new_rem, @new_add);
5120
+
5121
+ # Highlight if every removed line has a corresponding added line.
5122
+ # Combined diffs are not supported at this moment.
5123
+ if (!$is_combined && @$add > 0 && @$add == @$rem) {
5124
+ for (my $i = 0; $i < @$add; $i++) {
5125
+ my ($line_rem, $line_add) = format_rem_add_lines_pair(
5126
+ $rem->[$i], $add->[$i]);
5127
+ push @new_rem, $line_rem;
5128
+ push @new_add, $line_add;
5129
+ }
5130
+ } else {
5131
+ @new_rem = map { format_diff_line($_, 'rem') } @$rem;
5132
+ @new_add = map { format_diff_line($_, 'add') } @$add;
5133
+ }
5134
+
5135
+ @new_ctx = map { format_diff_line($_, 'ctx') } @$ctx;
5136
+
5137
+ return (\@new_ctx, \@new_rem, \@new_add);
5138
+}
5139
+
5140
# Print context lines and then rem/add lines.
5141
sub print_diff_lines {
5142
my ($ctx, $rem, $add, $diff_style, $is_combined) = @_;
5143
5144
+ ($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
5145
+ $is_combined);
5146
+
5147
if ($diff_style eq 'sidebyside' && !$is_combined) {
5148
print_sidebyside_diff_lines($ctx, $rem, $add);
5149
} else {
5175
foreach my $line_info (@chunk) {
5176
my ($class, $line) = @$line_info;
5177
5093
- $line = format_diff_line($line, $class, $from, $to);
5094
-
5178
# print chunk headers
5179
if ($class && $class eq 'chunk_header') {
5097
- print $line;
5180
+ print format_diff_line($line, $class, $from, $to);
5181
next;
5182
}
5183