@@ -144,22 +144,56 @@ impl From<io::Error> for ErrorKind {
144
144
}
145
145
}
146
146
147
+ /// Result of formatting a snippet of code along with ranges of lines that didn't get formatted,
148
+ /// i.e., that got returned as they were originally.
149
+ #[ derive( Debug ) ]
150
+ struct FormattedSnippet {
151
+ snippet : String ,
152
+ non_formatted_ranges : Vec < ( usize , usize ) > ,
153
+ }
154
+
155
+ impl FormattedSnippet {
156
+ /// In case the snippet needed to be wrapped in a function, this shifts down the ranges of
157
+ /// non-formatted code.
158
+ fn unwrap_code_block ( & mut self ) {
159
+ self . non_formatted_ranges
160
+ . iter_mut ( )
161
+ . for_each ( |( low, high) | {
162
+ * low -= 1 ;
163
+ * high -= 1 ;
164
+ } ) ;
165
+ }
166
+
167
+ /// Returns true if the line n did not get formatted.
168
+ fn is_line_non_formatted ( & self , n : usize ) -> bool {
169
+ self . non_formatted_ranges
170
+ . iter ( )
171
+ . any ( |( low, high) | * low <= n && n <= * high)
172
+ }
173
+ }
174
+
147
175
/// Reports on any issues that occurred during a run of Rustfmt.
148
176
///
149
177
/// Can be reported to the user via its `Display` implementation of `print_fancy`.
150
178
#[ derive( Clone ) ]
151
179
pub struct FormatReport {
152
180
// Maps stringified file paths to their associated formatting errors.
153
181
internal : Rc < RefCell < ( FormatErrorMap , ReportedErrors ) > > ,
182
+ non_formatted_ranges : Vec < ( usize , usize ) > ,
154
183
}
155
184
156
185
impl FormatReport {
157
186
fn new ( ) -> FormatReport {
158
187
FormatReport {
159
188
internal : Rc :: new ( RefCell :: new ( ( HashMap :: new ( ) , ReportedErrors :: default ( ) ) ) ) ,
189
+ non_formatted_ranges : Vec :: new ( ) ,
160
190
}
161
191
}
162
192
193
+ fn add_non_formatted_ranges ( & mut self , mut ranges : Vec < ( usize , usize ) > ) {
194
+ self . non_formatted_ranges . append ( & mut ranges) ;
195
+ }
196
+
163
197
fn append ( & self , f : FileName , mut v : Vec < FormattingError > ) {
164
198
self . track_errors ( & v) ;
165
199
self . internal
@@ -349,37 +383,44 @@ impl fmt::Display for FormatReport {
349
383
350
384
/// Format the given snippet. The snippet is expected to be *complete* code.
351
385
/// When we cannot parse the given snippet, this function returns `None`.
352
- fn format_snippet ( snippet : & str , config : & Config ) -> Option < String > {
386
+ fn format_snippet ( snippet : & str , config : & Config ) -> Option < FormattedSnippet > {
353
387
let mut config = config. clone ( ) ;
354
- let out = panic:: catch_unwind ( || {
388
+ panic:: catch_unwind ( || {
355
389
let mut out: Vec < u8 > = Vec :: with_capacity ( snippet. len ( ) * 2 ) ;
356
390
config. set ( ) . emit_mode ( config:: EmitMode :: Stdout ) ;
357
391
config. set ( ) . verbose ( Verbosity :: Quiet ) ;
358
392
config. set ( ) . hide_parse_errors ( true ) ;
359
- let formatting_error = {
393
+
394
+ let ( formatting_error, result) = {
360
395
let input = Input :: Text ( snippet. into ( ) ) ;
361
396
let mut session = Session :: new ( config, Some ( & mut out) ) ;
362
397
let result = session. format ( input) ;
363
- session. errors . has_macro_format_failure
364
- || session. out . as_ref ( ) . unwrap ( ) . is_empty ( ) && !snippet. is_empty ( )
365
- || result. is_err ( )
398
+ (
399
+ session. errors . has_macro_format_failure
400
+ || session. out . as_ref ( ) . unwrap ( ) . is_empty ( ) && !snippet. is_empty ( )
401
+ || result. is_err ( ) ,
402
+ result,
403
+ )
366
404
} ;
367
405
if formatting_error {
368
406
None
369
407
} else {
370
- Some ( out)
408
+ String :: from_utf8 ( out) . ok ( ) . map ( |snippet| FormattedSnippet {
409
+ snippet,
410
+ non_formatted_ranges : result. unwrap ( ) . non_formatted_ranges ,
411
+ } )
371
412
}
372
413
} )
373
- . ok ( ) ?? ; // The first try operator handles the error from catch_unwind,
374
- // whereas the second one handles None from the closure.
375
- String :: from_utf8 ( out ) . ok ( )
414
+ // Discard panics encountered while formatting the snippet
415
+ // The ? operator is needed to remove the extra Option
416
+ . ok ( ) ?
376
417
}
377
418
378
419
/// Format the given code block. Mainly targeted for code block in comment.
379
420
/// The code block may be incomplete (i.e. parser may be unable to parse it).
380
421
/// To avoid panic in parser, we wrap the code block with a dummy function.
381
422
/// The returned code block does *not* end with newline.
382
- fn format_code_block ( code_snippet : & str , config : & Config ) -> Option < String > {
423
+ fn format_code_block ( code_snippet : & str , config : & Config ) -> Option < FormattedSnippet > {
383
424
const FN_MAIN_PREFIX : & str = "fn main() {\n " ;
384
425
385
426
fn enclose_in_main_block ( s : & str , config : & Config ) -> String {
@@ -412,13 +453,18 @@ fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
412
453
config_with_unix_newline
413
454
. set ( )
414
455
. newline_style ( NewlineStyle :: Unix ) ;
415
- let formatted = format_snippet ( & snippet, & config_with_unix_newline) ?;
456
+ let mut formatted = format_snippet ( & snippet, & config_with_unix_newline) ?;
457
+ // Remove wrapping main block
458
+ formatted. unwrap_code_block ( ) ;
416
459
417
460
// Trim "fn main() {" on the first line and "}" on the last line,
418
461
// then unindent the whole code block.
419
- let block_len = formatted. rfind ( '}' ) . unwrap_or ( formatted. len ( ) ) ;
462
+ let block_len = formatted
463
+ . snippet
464
+ . rfind ( '}' )
465
+ . unwrap_or ( formatted. snippet . len ( ) ) ;
420
466
let mut is_indented = true ;
421
- for ( kind, ref line) in LineClasses :: new ( & formatted[ FN_MAIN_PREFIX . len ( ) ..block_len] ) {
467
+ for ( kind, ref line) in LineClasses :: new ( & formatted. snippet [ FN_MAIN_PREFIX . len ( ) ..block_len] ) {
422
468
if !is_first {
423
469
result. push ( '\n' ) ;
424
470
} else {
@@ -451,7 +497,10 @@ fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
451
497
result. push_str ( trimmed_line) ;
452
498
is_indented = !kind. is_string ( ) || line. ends_with ( '\\' ) ;
453
499
}
454
- Some ( result)
500
+ Some ( FormattedSnippet {
501
+ snippet : result,
502
+ non_formatted_ranges : formatted. non_formatted_ranges ,
503
+ } )
455
504
}
456
505
457
506
/// A session is a run of rustfmt across a single or multiple inputs.
@@ -571,10 +620,10 @@ mod unit_tests {
571
620
572
621
fn test_format_inner < F > ( formatter : F , input : & str , expected : & str ) -> bool
573
622
where
574
- F : Fn ( & str , & Config ) -> Option < String > ,
623
+ F : Fn ( & str , & Config ) -> Option < FormattedSnippet > ,
575
624
{
576
625
let output = formatter ( input, & Config :: default ( ) ) ;
577
- output. is_some ( ) && output. unwrap ( ) == expected
626
+ output. is_some ( ) && output. unwrap ( ) . snippet == expected
578
627
}
579
628
580
629
#[ test]
0 commit comments