@@ -10,16 +10,58 @@ pub enum Shift {
10
10
None ,
11
11
Left ( usize ) ,
12
12
Right ( usize ) ,
13
+ /// Strip leftmost whitespace that is common to all lines.
14
+ Auto ,
13
15
}
14
16
15
- fn shift_line ( l : & str , shift : Shift ) -> Cow < ' _ , str > {
17
+ #[ derive( PartialEq , Eq , Debug , Clone , Copy ) ]
18
+ enum ExplicitShift {
19
+ None ,
20
+ Left ( usize ) ,
21
+ Right ( usize ) ,
22
+ }
23
+
24
+ fn common_leading_ws ( lines : & [ String ] ) -> String {
25
+ let mut common_ws: Option < String > = None ;
26
+ for line in lines {
27
+ if line. is_empty ( ) {
28
+ // Don't include empty lines in the calculation.
29
+ continue ;
30
+ }
31
+ let ws = line. chars ( ) . take_while ( |c| c. is_whitespace ( ) ) ;
32
+ if let Some ( common) = common_ws {
33
+ common_ws = Some (
34
+ common
35
+ . chars ( )
36
+ . zip ( ws)
37
+ . take_while ( |( a, b) | a == b)
38
+ . map ( |( a, _b) | a)
39
+ . collect ( ) ,
40
+ ) ;
41
+ } else {
42
+ common_ws = Some ( ws. collect ( ) )
43
+ }
44
+ }
45
+ common_ws. unwrap_or_else ( String :: new)
46
+ }
47
+
48
+ fn calculate_shift ( lines : & [ String ] , shift : Shift ) -> ExplicitShift {
16
49
match shift {
17
- Shift :: None => Cow :: Borrowed ( l) ,
18
- Shift :: Right ( shift) => {
50
+ Shift :: None => ExplicitShift :: None ,
51
+ Shift :: Left ( l) => ExplicitShift :: Left ( l) ,
52
+ Shift :: Right ( r) => ExplicitShift :: Right ( r) ,
53
+ Shift :: Auto => ExplicitShift :: Left ( common_leading_ws ( lines) . len ( ) ) ,
54
+ }
55
+ }
56
+
57
+ fn shift_line ( l : & str , shift : ExplicitShift ) -> Cow < ' _ , str > {
58
+ match shift {
59
+ ExplicitShift :: None => Cow :: Borrowed ( l) ,
60
+ ExplicitShift :: Right ( shift) => {
19
61
let indent = " " . repeat ( shift) ;
20
62
Cow :: Owned ( format ! ( "{indent}{l}" ) )
21
63
}
22
- Shift :: Left ( skip) => {
64
+ ExplicitShift :: Left ( skip) => {
23
65
if l. chars ( ) . take ( skip) . any ( |c| !c. is_whitespace ( ) ) {
24
66
log:: error!( "left-shifting away non-whitespace" ) ;
25
67
}
@@ -30,6 +72,7 @@ fn shift_line(l: &str, shift: Shift) -> Cow<'_, str> {
30
72
}
31
73
32
74
fn shift_lines ( lines : & [ String ] , shift : Shift ) -> Vec < Cow < ' _ , str > > {
75
+ let shift = calculate_shift ( lines, shift) ;
33
76
lines. iter ( ) . map ( |l| shift_line ( l, shift) ) . collect ( )
34
77
}
35
78
@@ -160,20 +203,44 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
160
203
#[ cfg( test) ]
161
204
mod tests {
162
205
use super :: {
163
- shift_line, take_anchored_lines, take_anchored_lines_with_shift, take_lines ,
164
- take_lines_with_shift , take_rustdoc_include_anchored_lines , take_rustdoc_include_lines ,
165
- Shift ,
206
+ common_leading_ws , shift_line, take_anchored_lines, take_anchored_lines_with_shift,
207
+ take_lines , take_lines_with_shift , take_rustdoc_include_anchored_lines ,
208
+ take_rustdoc_include_lines , ExplicitShift , Shift ,
166
209
} ;
167
210
211
+ #[ test]
212
+ fn common_leading_ws_test ( ) {
213
+ let tests = [
214
+ ( [ " line1" , " line2" , " line3" ] , " " ) ,
215
+ ( [ " line1" , " line2" , "line3" ] , "" ) ,
216
+ ( [ "\t \t line1" , "\t \t line2" , "\t \t line3" ] , "\t \t " ) ,
217
+ ( [ "\t line1" , " \t line2" , " \t \t line3" ] , "" ) ,
218
+ ] ;
219
+ for ( lines, want) in tests {
220
+ let lines = lines. into_iter ( ) . map ( |l| l. to_string ( ) ) . collect :: < Vec < _ > > ( ) ;
221
+ let got = common_leading_ws ( & lines) ;
222
+ assert_eq ! ( got, want, "for input {lines:?}" ) ;
223
+ }
224
+ }
225
+
168
226
#[ test]
169
227
fn shift_line_test ( ) {
170
228
let s = " Line with 4 space intro" ;
171
- assert_eq ! ( shift_line( s, Shift :: None ) , s) ;
172
- assert_eq ! ( shift_line( s, Shift :: Left ( 4 ) ) , "Line with 4 space intro" ) ;
173
- assert_eq ! ( shift_line( s, Shift :: Left ( 2 ) ) , " Line with 4 space intro" ) ;
174
- assert_eq ! ( shift_line( s, Shift :: Left ( 6 ) ) , "ne with 4 space intro" ) ;
229
+ assert_eq ! ( shift_line( s, ExplicitShift :: None ) , s) ;
230
+ assert_eq ! (
231
+ shift_line( s, ExplicitShift :: Left ( 4 ) ) ,
232
+ "Line with 4 space intro"
233
+ ) ;
234
+ assert_eq ! (
235
+ shift_line( s, ExplicitShift :: Left ( 2 ) ) ,
236
+ " Line with 4 space intro"
237
+ ) ;
238
+ assert_eq ! (
239
+ shift_line( s, ExplicitShift :: Left ( 6 ) ) ,
240
+ "ne with 4 space intro"
241
+ ) ;
175
242
assert_eq ! (
176
- shift_line( s, Shift :: Right ( 2 ) ) ,
243
+ shift_line( s, ExplicitShift :: Right ( 2 ) ) ,
177
244
" Line with 4 space intro"
178
245
) ;
179
246
}
@@ -207,6 +274,10 @@ mod tests {
207
274
take_lines_with_shift( s, 1 ..3 , Shift :: Right ( 2 ) ) ,
208
275
" ipsum\n dolor"
209
276
) ;
277
+ assert_eq ! (
278
+ take_lines_with_shift( s, 1 ..3 , Shift :: Auto ) ,
279
+ "ipsum\n dolor"
280
+ ) ;
210
281
assert_eq ! ( take_lines_with_shift( s, 3 .., Shift :: None ) , " sit\n amet" ) ;
211
282
assert_eq ! (
212
283
take_lines_with_shift( s, 3 .., Shift :: Right ( 1 ) ) ,
@@ -217,6 +288,10 @@ mod tests {
217
288
take_lines_with_shift( s, ..3 , Shift :: None ) ,
218
289
" Lorem\n ipsum\n dolor"
219
290
) ;
291
+ assert_eq ! (
292
+ take_lines_with_shift( s, ..3 , Shift :: Auto ) ,
293
+ "Lorem\n ipsum\n dolor"
294
+ ) ;
220
295
assert_eq ! (
221
296
take_lines_with_shift( s, ..3 , Shift :: Right ( 4 ) ) ,
222
297
" Lorem\n ipsum\n dolor"
@@ -226,6 +301,10 @@ mod tests {
226
301
"rem\n sum\n dolor"
227
302
) ;
228
303
assert_eq ! ( take_lines_with_shift( s, .., Shift :: None ) , s) ;
304
+ assert_eq ! (
305
+ take_lines_with_shift( s, .., Shift :: Auto ) ,
306
+ "Lorem\n ipsum\n dolor\n sit\n amet"
307
+ ) ;
229
308
// corner cases
230
309
assert_eq ! ( take_lines_with_shift( s, 4 ..3 , Shift :: None ) , "" ) ;
231
310
assert_eq ! ( take_lines_with_shift( s, 4 ..3 , Shift :: Left ( 2 ) ) , "" ) ;
@@ -307,6 +386,10 @@ mod tests {
307
386
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
308
387
"dolor\n sit\n amet"
309
388
) ;
389
+ assert_eq ! (
390
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
391
+ "dolor\n sit\n amet"
392
+ ) ;
310
393
assert_eq ! (
311
394
take_anchored_lines_with_shift( s, "something" , Shift :: None ) ,
312
395
""
@@ -333,6 +416,10 @@ mod tests {
333
416
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
334
417
"dolor\n sit\n amet"
335
418
) ;
419
+ assert_eq ! (
420
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
421
+ "dolor\n sit\n amet"
422
+ ) ;
336
423
assert_eq ! (
337
424
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 4 ) ) ,
338
425
"lor\n t\n et"
@@ -346,18 +433,22 @@ mod tests {
346
433
""
347
434
) ;
348
435
349
- let s = " Lorem\n ANCHOR: test\n ipsum\n ANCHOR: test\n dolor\n sit\n amet\n ANCHOR_END: test\n lorem\n ipsum" ;
436
+ let s = " Lorem\n ANCHOR: test\n ipsum\n ANCHOR: test\n dolor\n \n \n sit\n amet\n ANCHOR_END: test\n lorem\n ipsum" ;
350
437
assert_eq ! (
351
438
take_anchored_lines_with_shift( s, "test" , Shift :: None ) ,
352
- " ipsum\n dolor\n sit\n amet"
439
+ " ipsum\n dolor\n \n \n sit\n amet"
353
440
) ;
354
441
assert_eq ! (
355
442
take_anchored_lines_with_shift( s, "test" , Shift :: Right ( 2 ) ) ,
356
- " ipsum\n dolor\n sit\n amet"
443
+ " ipsum\n dolor\n \n \n sit\n amet"
357
444
) ;
358
445
assert_eq ! (
359
446
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
360
- "ipsum\n dolor\n sit\n amet"
447
+ "ipsum\n dolor\n \n \n sit\n amet"
448
+ ) ;
449
+ assert_eq ! (
450
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
451
+ "ipsum\n dolor\n \n \n sit\n amet"
361
452
) ;
362
453
assert_eq ! (
363
454
take_anchored_lines_with_shift( s, "something" , Shift :: None ) ,
@@ -371,6 +462,10 @@ mod tests {
371
462
take_anchored_lines_with_shift( s, "something" , Shift :: Left ( 2 ) ) ,
372
463
""
373
464
) ;
465
+ assert_eq ! (
466
+ take_anchored_lines_with_shift( s, "something" , Shift :: Auto ) ,
467
+ ""
468
+ ) ;
374
469
375
470
// Include non-ASCII.
376
471
let s = " Lorem\n ANCHOR: test2\n ípsum\n ANCHOR: test\n dôlor\n sit\n amet\n ANCHOR_END: test\n lorem\n ANCHOR_END:test2\n ipsum" ;
0 commit comments