@@ -27,6 +27,7 @@ extern crate unicode_segmentation;
27
27
use std:: collections:: HashMap ;
28
28
use std:: fmt;
29
29
use std:: io:: { self , stdout, Write } ;
30
+ use std:: iter:: repeat;
30
31
use std:: ops:: { Add , Sub } ;
31
32
use std:: path:: { Path , PathBuf } ;
32
33
use std:: rc:: Rc ;
@@ -42,7 +43,7 @@ use checkstyle::{output_footer, output_header};
42
43
use config:: Config ;
43
44
use filemap:: FileMap ;
44
45
use issues:: { BadIssueSeeker , Issue } ;
45
- use utils:: { mk_sp, outer_attributes} ;
46
+ use utils:: { isatty , mk_sp, outer_attributes} ;
46
47
use visitor:: FmtVisitor ;
47
48
48
49
pub use self :: summary:: Summary ;
@@ -456,7 +457,7 @@ impl fmt::Display for ErrorKind {
456
457
match * self {
457
458
ErrorKind :: LineOverflow ( found, maximum) => write ! (
458
459
fmt,
459
- "line exceeded maximum length (maximum: {}, found: {})" ,
460
+ "line exceeded maximum width (maximum: {}, found: {})" ,
460
461
maximum,
461
462
found
462
463
) ,
@@ -468,22 +469,43 @@ impl fmt::Display for ErrorKind {
468
469
469
470
// Formatting errors that are identified *after* rustfmt has run.
470
471
pub struct FormattingError {
471
- line : u32 ,
472
+ line : usize ,
472
473
kind : ErrorKind ,
474
+ is_comment : bool ,
475
+ line_buffer : String ,
473
476
}
474
477
475
478
impl FormattingError {
476
479
fn msg_prefix ( & self ) -> & str {
477
480
match self . kind {
478
- ErrorKind :: LineOverflow ( ..) | ErrorKind :: TrailingWhitespace => "Rustfmt failed at " ,
481
+ ErrorKind :: LineOverflow ( ..) | ErrorKind :: TrailingWhitespace => "error: " ,
479
482
ErrorKind :: BadIssue ( _) => "WARNING:" ,
480
483
}
481
484
}
482
485
483
- fn msg_suffix ( & self ) -> & str {
486
+ fn msg_suffix ( & self ) -> String {
484
487
match self . kind {
485
- ErrorKind :: LineOverflow ( ..) | ErrorKind :: TrailingWhitespace => "(sorry)" ,
486
- ErrorKind :: BadIssue ( _) => "" ,
488
+ ErrorKind :: LineOverflow ( ..) if self . is_comment => format ! (
489
+ "use `error_on_lineoverflow_comments = false` to suppress \
490
+ the warning against line comments\n ",
491
+ ) ,
492
+ _ => String :: from ( "" ) ,
493
+ }
494
+ }
495
+
496
+ // (space, target)
497
+ pub fn format_len ( & self ) -> ( usize , usize ) {
498
+ match self . kind {
499
+ ErrorKind :: LineOverflow ( found, max) => ( max, found - max) ,
500
+ ErrorKind :: TrailingWhitespace => {
501
+ let trailing_ws_len = self . line_buffer
502
+ . chars ( )
503
+ . rev ( )
504
+ . take_while ( |c| c. is_whitespace ( ) )
505
+ . count ( ) ;
506
+ ( self . line_buffer . len ( ) - trailing_ws_len, trailing_ws_len)
507
+ }
508
+ _ => ( 0 , 0 ) , // unreachable
487
509
}
488
510
}
489
511
}
@@ -510,24 +532,127 @@ impl FormatReport {
510
532
pub fn has_warnings ( & self ) -> bool {
511
533
self . warning_count ( ) > 0
512
534
}
535
+
536
+ pub fn print_warnings_fancy (
537
+ & self ,
538
+ mut t : Box < term:: Terminal < Output = io:: Stderr > > ,
539
+ ) -> Result < ( ) , term:: Error > {
540
+ for ( file, errors) in & self . file_error_map {
541
+ for error in errors {
542
+ let prefix_space_len = error. line . to_string ( ) . len ( ) ;
543
+ let prefix_spaces: String = repeat ( " " ) . take ( 1 + prefix_space_len) . collect ( ) ;
544
+
545
+ // First line: the overview of error
546
+ t. fg ( term:: color:: RED ) ?;
547
+ t. attr ( term:: Attr :: Bold ) ?;
548
+ write ! ( t, "{} " , error. msg_prefix( ) ) ?;
549
+ t. reset ( ) ?;
550
+ t. attr ( term:: Attr :: Bold ) ?;
551
+ write ! ( t, "{}\n " , error. kind) ?;
552
+
553
+ // Second line: file info
554
+ write ! ( t, "{}--> " , & prefix_spaces[ 1 ..] ) ?;
555
+ t. reset ( ) ?;
556
+ write ! ( t, "{}:{}\n " , file, error. line) ?;
557
+
558
+ // Third to fifth lines: show the line which triggered error, if available.
559
+ if !error. line_buffer . is_empty ( ) {
560
+ let ( space_len, target_len) = error. format_len ( ) ;
561
+ t. attr ( term:: Attr :: Bold ) ?;
562
+ write ! ( t, "{}|\n {} | " , prefix_spaces, error. line) ?;
563
+ t. reset ( ) ?;
564
+ write ! ( t, "{}\n " , error. line_buffer) ?;
565
+ t. attr ( term:: Attr :: Bold ) ?;
566
+ write ! ( t, "{}| " , prefix_spaces) ?;
567
+ t. fg ( term:: color:: RED ) ?;
568
+ write ! ( t, "{}\n " , target_str( space_len, target_len) ) ?;
569
+ t. reset ( ) ?;
570
+ }
571
+
572
+ // The last line: show note if available.
573
+ let msg_suffix = error. msg_suffix ( ) ;
574
+ if !msg_suffix. is_empty ( ) {
575
+ t. attr ( term:: Attr :: Bold ) ?;
576
+ write ! ( t, "{}= note: " , prefix_spaces) ?;
577
+ t. reset ( ) ?;
578
+ write ! ( t, "{}\n " , error. msg_suffix( ) ) ?;
579
+ } else {
580
+ write ! ( t, "\n " ) ?;
581
+ }
582
+ t. reset ( ) ?;
583
+ }
584
+ }
585
+
586
+ if !self . file_error_map . is_empty ( ) {
587
+ t. attr ( term:: Attr :: Bold ) ?;
588
+ write ! ( t, "warning: " ) ?;
589
+ t. reset ( ) ?;
590
+ write ! (
591
+ t,
592
+ "rustfmt may have failed to format. See previous {} errors.\n \n " ,
593
+ self . warning_count( ) ,
594
+ ) ?;
595
+ }
596
+
597
+ Ok ( ( ) )
598
+ }
599
+ }
600
+
601
+ fn target_str ( space_len : usize , target_len : usize ) -> String {
602
+ let empty_line: String = repeat ( " " ) . take ( space_len) . collect ( ) ;
603
+ let overflowed: String = repeat ( "^" ) . take ( target_len) . collect ( ) ;
604
+ empty_line + & overflowed
513
605
}
514
606
515
607
impl fmt:: Display for FormatReport {
516
608
// Prints all the formatting errors.
517
609
fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> Result < ( ) , fmt:: Error > {
518
610
for ( file, errors) in & self . file_error_map {
519
611
for error in errors {
612
+ let prefix_space_len = error. line . to_string ( ) . len ( ) ;
613
+ let prefix_spaces: String = repeat ( " " ) . take ( 1 + prefix_space_len) . collect ( ) ;
614
+
615
+ let error_line_buffer = if error. line_buffer . is_empty ( ) {
616
+ String :: from ( " " )
617
+ } else {
618
+ let ( space_len, target_len) = error. format_len ( ) ;
619
+ format ! (
620
+ "{}|\n {} | {}\n {}| {}" ,
621
+ prefix_spaces,
622
+ error. line,
623
+ error. line_buffer,
624
+ prefix_spaces,
625
+ target_str( space_len, target_len)
626
+ )
627
+ } ;
628
+
629
+ let error_info = format ! ( "{} {}" , error. msg_prefix( ) , error. kind) ;
630
+ let file_info = format ! ( "{}--> {}:{}" , & prefix_spaces[ 1 ..] , file, error. line) ;
631
+ let msg_suffix = error. msg_suffix ( ) ;
632
+ let note = if msg_suffix. is_empty ( ) {
633
+ String :: new ( )
634
+ } else {
635
+ format ! ( "{}note= " , prefix_spaces)
636
+ } ;
637
+
520
638
write ! (
521
639
fmt,
522
- "{} {}:{}: {} {}\n " ,
523
- error . msg_prefix ( ) ,
524
- file ,
525
- error . line ,
526
- error . kind ,
640
+ "{}\n {} \n {} \n {} {}\n " ,
641
+ error_info ,
642
+ file_info ,
643
+ error_line_buffer ,
644
+ note ,
527
645
error. msg_suffix( )
528
646
) ?;
529
647
}
530
648
}
649
+ if !self . file_error_map . is_empty ( ) {
650
+ write ! (
651
+ fmt,
652
+ "warning: rustfmt may have failed to format. See previous {} errors.\n " ,
653
+ self . warning_count( ) ,
654
+ ) ?;
655
+ }
531
656
Ok ( ( ) )
532
657
}
533
658
}
@@ -601,6 +726,7 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
601
726
let mut issue_seeker = BadIssueSeeker :: new ( config. report_todo ( ) , config. report_fixme ( ) ) ;
602
727
let mut prev_char: Option < char > = None ;
603
728
let mut is_comment = false ;
729
+ let mut line_buffer = String :: with_capacity ( config. max_width ( ) * 2 ) ;
604
730
605
731
for ( c, b) in text. chars ( ) {
606
732
if c == '\r' {
@@ -615,6 +741,8 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
615
741
errors. push ( FormattingError {
616
742
line : cur_line,
617
743
kind : ErrorKind :: BadIssue ( issue) ,
744
+ is_comment : false ,
745
+ line_buffer : String :: new ( ) ,
618
746
} ) ;
619
747
}
620
748
}
@@ -623,7 +751,7 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
623
751
if format_line {
624
752
// Check for (and record) trailing whitespace.
625
753
if let Some ( lw) = last_wspace {
626
- trims. push ( ( cur_line, lw, b) ) ;
754
+ trims. push ( ( cur_line, lw, b, line_buffer . clone ( ) ) ) ;
627
755
line_len -= 1 ;
628
756
}
629
757
@@ -634,6 +762,8 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
634
762
errors. push ( FormattingError {
635
763
line : cur_line,
636
764
kind : ErrorKind :: LineOverflow ( line_len, config. max_width ( ) ) ,
765
+ is_comment : is_comment,
766
+ line_buffer : line_buffer. clone ( ) ,
637
767
} ) ;
638
768
}
639
769
}
@@ -644,6 +774,7 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
644
774
last_wspace = None ;
645
775
prev_char = None ;
646
776
is_comment = false ;
777
+ line_buffer. clear ( ) ;
647
778
} else {
648
779
newline_count = 0 ;
649
780
line_len += 1 ;
@@ -661,6 +792,7 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
661
792
last_wspace = None ;
662
793
}
663
794
prev_char = Some ( c) ;
795
+ line_buffer. push ( c) ;
664
796
}
665
797
}
666
798
@@ -670,10 +802,12 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
670
802
text. truncate ( line) ;
671
803
}
672
804
673
- for & ( l, _, _) in & trims {
805
+ for & ( l, _, _, ref b ) in & trims {
674
806
errors. push ( FormattingError {
675
807
line : l,
676
808
kind : ErrorKind :: TrailingWhitespace ,
809
+ is_comment : false ,
810
+ line_buffer : b. clone ( ) ,
677
811
} ) ;
678
812
}
679
813
@@ -803,7 +937,15 @@ pub fn run(input: Input, config: &Config) -> Summary {
803
937
output_footer ( out, config. write_mode ( ) ) . ok ( ) ;
804
938
805
939
if report. has_warnings ( ) {
806
- msg ! ( "{}" , report) ;
940
+ match term:: stderr ( ) {
941
+ Some ( ref t) if isatty ( ) && t. supports_color ( ) => {
942
+ match report. print_warnings_fancy ( term:: stderr ( ) . unwrap ( ) ) {
943
+ Ok ( ..) => ( ) ,
944
+ Err ( ..) => panic ! ( "Unable to write to stderr: {}" , report) ,
945
+ }
946
+ }
947
+ _ => msg ! ( "{}" , report) ,
948
+ }
807
949
}
808
950
809
951
summary
0 commit comments