@@ -1028,6 +1028,171 @@ sub color_diff {
1028
1028
} @_ ;
1029
1029
}
1030
1030
1031
+ sub label_hunk_lines {
1032
+ local $_ ;
1033
+ my $hunk = shift ;
1034
+ my $i = 0;
1035
+ my $labels = [ map { / ^[-+]/ ? ++$i : 0 } @{$hunk -> {TEXT }} ];
1036
+ if ($i > 1) {
1037
+ @{$hunk }{qw( LABELS MAX_LABEL) } = ($labels , $i );
1038
+ return 1;
1039
+ }
1040
+ return 0;
1041
+ }
1042
+
1043
+ sub select_hunk_lines {
1044
+ my ($hunk , $selected ) = @_ ;
1045
+ my ($text , $labels ) = @{$hunk }{qw( TEXT LABELS) };
1046
+ my ($i , $o_cnt , $n_cnt ) = (0, 0, 0);
1047
+ my ($push_eol , @newtext );
1048
+ # Lines with this mode will become context lines if they are
1049
+ # not selected
1050
+ my $context_mode = $patch_mode_flavour {IS_REVERSE } ? ' +' : ' -' ;
1051
+ for $i (1..$# {$text }) {
1052
+ my $mode = substr ($text -> [$i ], 0, 1);
1053
+ if ($mode eq ' \\ ' ) {
1054
+ push @newtext , $text -> [$i ] if ($push_eol );
1055
+ undef $push_eol ;
1056
+ } elsif ($labels -> [$i ] and $selected -> [$labels -> [$i ]]) {
1057
+ push @newtext , $text -> [$i ];
1058
+ if ($mode eq ' +' ) {
1059
+ $n_cnt ++;
1060
+ } else {
1061
+ $o_cnt ++;
1062
+ }
1063
+ $push_eol = 1;
1064
+ } elsif ($mode eq ' ' or $mode eq $context_mode ) {
1065
+ push @newtext , ' ' . substr ($text -> [$i ], 1);
1066
+ $o_cnt ++; $n_cnt ++;
1067
+ $push_eol = 1;
1068
+ } else {
1069
+ undef $push_eol ;
1070
+ }
1071
+ }
1072
+ my ($o_ofs , $orig_o_cnt , $n_ofs , $orig_n_cnt ) =
1073
+ parse_hunk_header($text -> [0]);
1074
+ unshift @newtext , format_hunk_header($o_ofs , $o_cnt , $n_ofs , $n_cnt );
1075
+ my $newhunk = {
1076
+ TEXT => \@newtext ,
1077
+ DISPLAY => [ color_diff(@newtext ) ],
1078
+ OFS_DELTA => $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt ,
1079
+ TYPE => $hunk -> {TYPE },
1080
+ USE => 1,
1081
+ };
1082
+ # If this hunk has previously been edited add the offset delta
1083
+ # of the old hunk to get the real delta from the original
1084
+ # hunk.
1085
+ if ($hunk -> {OFS_DELTA }) {
1086
+ $newhunk -> {OFS_DELTA } += $hunk -> {OFS_DELTA };
1087
+ }
1088
+ return $newhunk ;
1089
+ }
1090
+
1091
+ sub check_hunk_label {
1092
+ my ($max_label , $label ) = ($_ [0]-> {MAX_LABEL }, $_ [1]);
1093
+ if ($label < 1 or $label > $max_label ) {
1094
+ error_msg sprintf (__(" invalid hunk line '%d '\n " ), $label );
1095
+ return 0;
1096
+ }
1097
+ return 1;
1098
+ }
1099
+
1100
+ sub split_hunk_selection {
1101
+ local $_ ;
1102
+ my @fields = @_ ;
1103
+ my @ret ;
1104
+ for (@fields ) {
1105
+ while ($_ ne ' ' ) {
1106
+ if (/ ^[0-9]-$ / ) {
1107
+ push @ret , $_ ;
1108
+ last ;
1109
+ } elsif (/ ^([0-9](?:-[0-9])?)(.*)/ ) {
1110
+ push @ret , $1 ;
1111
+ $_ = $2 ;
1112
+ } else {
1113
+ error_msg sprintf
1114
+ __(" invalid hunk line '%s '\n " ),
1115
+ substr ($_ , 0, 1);
1116
+ return ();
1117
+ }
1118
+ }
1119
+ }
1120
+ return @ret ;
1121
+ }
1122
+
1123
+ sub parse_hunk_selection {
1124
+ local $_ ;
1125
+ my ($hunk , $line ) = @_ ;
1126
+ my ($max_label , $invert ) = ($hunk -> {MAX_LABEL }, undef );
1127
+ my @selected = (0) x ($max_label + 1);
1128
+ my @fields = split (/ [,\s ]+/ , $line );
1129
+ if ($fields [0] =~ / ^-(.*)/ ) {
1130
+ $invert = 1;
1131
+ if ($1 ne ' ' ) {
1132
+ $fields [0] = $1 ;
1133
+ } else {
1134
+ shift @fields ;
1135
+ unless (@fields ) {
1136
+ error_msg __(" no lines to invert\n " );
1137
+ return undef ;
1138
+ }
1139
+ }
1140
+ }
1141
+ if ($max_label < 10) {
1142
+ @fields = split_hunk_selection(@fields ) or return undef ;
1143
+ }
1144
+ for (@fields ) {
1145
+ if (my ($lo , $hi ) = /^([0-9]+)-([0-9]*)$/ ) {
1146
+ if ($hi eq ' ' ) {
1147
+ $hi = $max_label ;
1148
+ }
1149
+ check_hunk_label($hunk , $lo ) or return undef ;
1150
+ check_hunk_label($hunk , $hi ) or return undef ;
1151
+ if ($hi < $lo ) {
1152
+ ($lo , $hi ) = ($hi , $lo );
1153
+ }
1154
+ @selected [$lo ..$hi ] = (1) x (1 + $hi - $lo );
1155
+ } elsif (/ ^([0-9]+)$ / ) {
1156
+ check_hunk_label($hunk , $1 ) or return undef ;
1157
+ $selected [$1 ] = 1;
1158
+ } else {
1159
+ error_msg sprintf (__(" invalid hunk line '%s '\n " ), $_ );
1160
+ return undef ;
1161
+ }
1162
+ }
1163
+ if ($invert ) {
1164
+ @selected = map { !$_ } @selected ;
1165
+ }
1166
+ return \@selected ;
1167
+ }
1168
+
1169
+ sub display_hunk_lines {
1170
+ my ($display , $labels , $max_label ) =
1171
+ @{$_ [0]}{qw( DISPLAY LABELS MAX_LABEL) };
1172
+ my $width = int (log ($max_label ) / log (10)) + 1;
1173
+ my $padding = ' ' x ($width + 1);
1174
+ for my $i (0..$# {$display }) {
1175
+ if ($labels -> [$i ]) {
1176
+ printf ' %*d %s' , $width , $labels -> [$i ], $display -> [$i ];
1177
+ } else {
1178
+ print $padding . $display -> [$i ];
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ sub select_lines_loop {
1184
+ my $hunk = shift ;
1185
+ display_hunk_lines($hunk );
1186
+ my $selection = undef ;
1187
+ until (defined $selection ) {
1188
+ print colored $prompt_color , __(" select lines? " );
1189
+ my $text = <STDIN >;
1190
+ defined $text and $text =~ / \S / or return undef ;
1191
+ $selection = parse_hunk_selection($hunk , $text );
1192
+ }
1193
+ return select_hunk_lines($hunk , $selection );
1194
+ }
1195
+
1031
1196
my %edit_hunk_manually_modes = (
1032
1197
stage => N__(
1033
1198
" If the patch applies cleanly, the edited hunk will immediately be
@@ -1276,6 +1441,7 @@ sub help_patch_cmd {
1276
1441
J - leave this hunk undecided, see next hunk
1277
1442
k - leave this hunk undecided, see previous undecided hunk
1278
1443
K - leave this hunk undecided, see previous hunk
1444
+ l - select hunk lines to use
1279
1445
s - split the current hunk into smaller hunks
1280
1446
e - manually edit the current hunk
1281
1447
? - print help
@@ -1492,6 +1658,9 @@ sub patch_update_file {
1492
1658
if ($hunk [$ix ]{TYPE } eq ' hunk' ) {
1493
1659
$other .= ' ,e' ;
1494
1660
}
1661
+ if (label_hunk_lines($hunk [$ix ])) {
1662
+ $other .= ' ,l' ;
1663
+ }
1495
1664
for (@{$hunk [$ix ]{DISPLAY }}) {
1496
1665
print ;
1497
1666
}
@@ -1639,6 +1808,18 @@ sub patch_update_file {
1639
1808
next ;
1640
1809
}
1641
1810
}
1811
+ elsif ($line =~ / ^l/ ) {
1812
+ unless ($other =~ / l/ ) {
1813
+ error_msg __(" Cannot select line by line\n " );
1814
+ next ;
1815
+ }
1816
+ my $newhunk = select_lines_loop($hunk [$ix ]);
1817
+ if ($newhunk ) {
1818
+ splice @hunk , $ix , 1, $newhunk ;
1819
+ } else {
1820
+ next ;
1821
+ }
1822
+ }
1642
1823
elsif ($line =~ / ^s/ ) {
1643
1824
unless ($other =~ / s/ ) {
1644
1825
error_msg __(" Sorry, cannot split this hunk\n " );
0 commit comments