@@ -10,16 +10,54 @@ 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
+ let ws = line. chars ( ) . take_while ( |c| c. is_whitespace ( ) ) ;
28
+ if let Some ( common) = common_ws {
29
+ common_ws = Some (
30
+ common
31
+ . chars ( )
32
+ . zip ( ws)
33
+ . take_while ( |( a, b) | a == b)
34
+ . map ( |( a, _b) | a)
35
+ . collect ( ) ,
36
+ ) ;
37
+ } else {
38
+ common_ws = Some ( ws. collect ( ) )
39
+ }
40
+ }
41
+ common_ws. unwrap_or_else ( String :: new)
42
+ }
43
+
44
+ fn calculate_shift ( lines : & [ String ] , shift : Shift ) -> ExplicitShift {
45
+ match shift {
46
+ Shift :: None => ExplicitShift :: None ,
47
+ Shift :: Left ( l) => ExplicitShift :: Left ( l) ,
48
+ Shift :: Right ( r) => ExplicitShift :: Right ( r) ,
49
+ Shift :: Auto => ExplicitShift :: Left ( common_leading_ws ( lines) . len ( ) ) ,
50
+ }
51
+ }
52
+
53
+ fn shift_line ( l : & str , shift : ExplicitShift ) -> Cow < ' _ , str > {
16
54
match shift {
17
- Shift :: None => Cow :: Borrowed ( l) ,
18
- Shift :: Right ( shift) => {
55
+ ExplicitShift :: None => Cow :: Borrowed ( l) ,
56
+ ExplicitShift :: Right ( shift) => {
19
57
let indent = " " . repeat ( shift) ;
20
58
Cow :: Owned ( format ! ( "{indent}{l}" ) )
21
59
}
22
- Shift :: Left ( skip) => {
60
+ ExplicitShift :: Left ( skip) => {
23
61
if l. chars ( ) . take ( skip) . any ( |c| !c. is_whitespace ( ) ) {
24
62
log:: error!( "left-shifting away non-whitespace" ) ;
25
63
}
@@ -30,6 +68,7 @@ fn shift_line(l: &str, shift: Shift) -> Cow<'_, str> {
30
68
}
31
69
32
70
fn shift_lines ( lines : & [ String ] , shift : Shift ) -> Vec < Cow < ' _ , str > > {
71
+ let shift = calculate_shift ( lines, shift) ;
33
72
lines. iter ( ) . map ( |l| shift_line ( l, shift) ) . collect ( )
34
73
}
35
74
@@ -160,20 +199,44 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
160
199
#[ cfg( test) ]
161
200
mod tests {
162
201
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 ,
202
+ common_leading_ws , shift_line, take_anchored_lines, take_anchored_lines_with_shift,
203
+ take_lines , take_lines_with_shift , take_rustdoc_include_anchored_lines ,
204
+ take_rustdoc_include_lines , ExplicitShift , Shift ,
166
205
} ;
167
206
207
+ #[ test]
208
+ fn common_leading_ws_test ( ) {
209
+ let tests = [
210
+ ( [ " line1" , " line2" , " line3" ] , " " ) ,
211
+ ( [ " line1" , " line2" , "line3" ] , "" ) ,
212
+ ( [ "\t \t line1" , "\t \t line2" , "\t \t line3" ] , "\t \t " ) ,
213
+ ( [ "\t line1" , " \t line2" , " \t \t line3" ] , "" ) ,
214
+ ] ;
215
+ for ( lines, want) in tests {
216
+ let lines = lines. into_iter ( ) . map ( |l| l. to_string ( ) ) . collect :: < Vec < _ > > ( ) ;
217
+ let got = common_leading_ws ( & lines) ;
218
+ assert_eq ! ( got, want, "for input {lines:?}" ) ;
219
+ }
220
+ }
221
+
168
222
#[ test]
169
223
fn shift_line_test ( ) {
170
224
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" ) ;
225
+ assert_eq ! ( shift_line( s, ExplicitShift :: None ) , s) ;
226
+ assert_eq ! (
227
+ shift_line( s, ExplicitShift :: Left ( 4 ) ) ,
228
+ "Line with 4 space intro"
229
+ ) ;
230
+ assert_eq ! (
231
+ shift_line( s, ExplicitShift :: Left ( 2 ) ) ,
232
+ " Line with 4 space intro"
233
+ ) ;
175
234
assert_eq ! (
176
- shift_line( s, Shift :: Right ( 2 ) ) ,
235
+ shift_line( s, ExplicitShift :: Left ( 6 ) ) ,
236
+ "ne with 4 space intro"
237
+ ) ;
238
+ assert_eq ! (
239
+ shift_line( s, ExplicitShift :: Right ( 2 ) ) ,
177
240
" Line with 4 space intro"
178
241
) ;
179
242
}
@@ -207,6 +270,10 @@ mod tests {
207
270
take_lines_with_shift( s, 1 ..3 , Shift :: Right ( 2 ) ) ,
208
271
" ipsum\n dolor"
209
272
) ;
273
+ assert_eq ! (
274
+ take_lines_with_shift( s, 1 ..3 , Shift :: Auto ) ,
275
+ "ipsum\n dolor"
276
+ ) ;
210
277
assert_eq ! ( take_lines_with_shift( s, 3 .., Shift :: None ) , " sit\n amet" ) ;
211
278
assert_eq ! (
212
279
take_lines_with_shift( s, 3 .., Shift :: Right ( 1 ) ) ,
@@ -217,6 +284,10 @@ mod tests {
217
284
take_lines_with_shift( s, ..3 , Shift :: None ) ,
218
285
" Lorem\n ipsum\n dolor"
219
286
) ;
287
+ assert_eq ! (
288
+ take_lines_with_shift( s, ..3 , Shift :: Auto ) ,
289
+ "Lorem\n ipsum\n dolor"
290
+ ) ;
220
291
assert_eq ! (
221
292
take_lines_with_shift( s, ..3 , Shift :: Right ( 4 ) ) ,
222
293
" Lorem\n ipsum\n dolor"
@@ -226,6 +297,10 @@ mod tests {
226
297
"rem\n sum\n dolor"
227
298
) ;
228
299
assert_eq ! ( take_lines_with_shift( s, .., Shift :: None ) , s) ;
300
+ assert_eq ! (
301
+ take_lines_with_shift( s, .., Shift :: Auto ) ,
302
+ "Lorem\n ipsum\n dolor\n sit\n amet"
303
+ ) ;
229
304
// corner cases
230
305
assert_eq ! ( take_lines_with_shift( s, 4 ..3 , Shift :: None ) , "" ) ;
231
306
assert_eq ! ( take_lines_with_shift( s, 4 ..3 , Shift :: Left ( 2 ) ) , "" ) ;
@@ -307,6 +382,10 @@ mod tests {
307
382
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
308
383
"dolor\n sit\n amet"
309
384
) ;
385
+ assert_eq ! (
386
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
387
+ "dolor\n sit\n amet"
388
+ ) ;
310
389
assert_eq ! (
311
390
take_anchored_lines_with_shift( s, "something" , Shift :: None ) ,
312
391
""
@@ -333,6 +412,10 @@ mod tests {
333
412
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
334
413
"dolor\n sit\n amet"
335
414
) ;
415
+ assert_eq ! (
416
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
417
+ "dolor\n sit\n amet"
418
+ ) ;
336
419
assert_eq ! (
337
420
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 4 ) ) ,
338
421
"lor\n t\n et"
@@ -359,6 +442,10 @@ mod tests {
359
442
take_anchored_lines_with_shift( s, "test" , Shift :: Left ( 2 ) ) ,
360
443
"ipsum\n dolor\n sit\n amet"
361
444
) ;
445
+ assert_eq ! (
446
+ take_anchored_lines_with_shift( s, "test" , Shift :: Auto ) ,
447
+ "ipsum\n dolor\n sit\n amet"
448
+ ) ;
362
449
assert_eq ! (
363
450
take_anchored_lines_with_shift( s, "something" , Shift :: None ) ,
364
451
""
@@ -371,6 +458,10 @@ mod tests {
371
458
take_anchored_lines_with_shift( s, "something" , Shift :: Left ( 2 ) ) ,
372
459
""
373
460
) ;
461
+ assert_eq ! (
462
+ take_anchored_lines_with_shift( s, "something" , Shift :: Auto ) ,
463
+ ""
464
+ ) ;
374
465
375
466
// Include non-ASCII.
376
467
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