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