@@ -256,6 +256,26 @@ impl Sink for FastmodSink {
256
256
}
257
257
}
258
258
259
+ fn to_char_boundary ( s : & str , mut index : usize ) -> usize {
260
+ while index < s. len ( ) && !s. is_char_boundary ( index) {
261
+ index += 1 ;
262
+ }
263
+ debug_assert ! (
264
+ index > s. len( ) || s. is_char_boundary( index) ,
265
+ "index: {}, len: {}" ,
266
+ index,
267
+ s. len( )
268
+ ) ;
269
+ index
270
+ }
271
+
272
+ fn backward_to_char_boundary ( s : & str , mut index : usize ) -> usize {
273
+ while !s. is_char_boundary ( index) {
274
+ index -= 1 ;
275
+ }
276
+ index
277
+ }
278
+
259
279
impl Fastmod {
260
280
fn new ( accept_all : bool , hidden : bool , print_changed_files : bool ) -> Fastmod {
261
281
Fastmod {
@@ -342,7 +362,10 @@ impl Fastmod {
342
362
// Avoid generating index of -1 when start
343
363
// == end == offset = 0 for a zero-length
344
364
// match.
345
- mat. end ( ) + offset - if is_zero_length_match { 0 } else { 1 } ,
365
+ backward_to_char_boundary (
366
+ & contents,
367
+ mat. end ( ) + offset - if is_zero_length_match { 0 } else { 1 } ,
368
+ ) ,
346
369
) ;
347
370
let accepted = self . ask_about_patch (
348
371
path,
@@ -352,15 +375,18 @@ impl Fastmod {
352
375
& new_contents,
353
376
) ?;
354
377
if accepted {
355
- offset = offset
378
+ offset = to_char_boundary (
379
+ & contents,
380
+ offset
356
381
+ mat. start ( )
357
382
+ subst. len ( )
358
383
// Ensure forward progress when there
359
384
// is a zero-length match.
360
- + if is_zero_length_match { 1 } else { 0 } ;
385
+ + if is_zero_length_match { 1 } else { 0 } ,
386
+ ) ;
361
387
} else {
362
388
// Advance to the next character after the match.
363
- offset = offset + mat. end ( ) + 1 ;
389
+ offset = to_char_boundary ( & contents , offset + mat. end ( ) + 1 ) ;
364
390
}
365
391
}
366
392
}
@@ -1109,4 +1135,21 @@ mod tests {
1109
1135
}
1110
1136
}
1111
1137
}
1138
+
1139
+ #[ test]
1140
+ fn test_replace_next_to_unicode_character ( ) {
1141
+ let contents = "I have “unicodequotes”" ;
1142
+ let dir = create_test_files ( & [ ( "foo.txt" , contents) ] ) ;
1143
+ Command :: cargo_bin ( "fastmod" )
1144
+ . unwrap ( )
1145
+ . args ( & [
1146
+ "quotes" ,
1147
+ "characters" ,
1148
+ "--dir" ,
1149
+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
1150
+ ] )
1151
+ . write_stdin ( "n\n " )
1152
+ . assert ( )
1153
+ . success ( ) ;
1154
+ }
1112
1155
}
0 commit comments