@@ -14,7 +14,10 @@ use rustc_span::{MultiSpan, SourceFile, Span};
14
14
15
15
use crate :: snippet:: { Annotation , AnnotationType , Line , MultilineAnnotation , Style , StyledString } ;
16
16
use crate :: styled_buffer:: StyledBuffer ;
17
- use crate :: { CodeSuggestion , Diagnostic , DiagnosticId , Level , SubDiagnostic , SuggestionStyle } ;
17
+ use crate :: {
18
+ CodeSuggestion , Diagnostic , DiagnosticId , Level , SubDiagnostic , SubstitutionHighlight ,
19
+ SuggestionStyle ,
20
+ } ;
18
21
19
22
use rustc_lint_defs:: pluralize;
20
23
@@ -1590,25 +1593,38 @@ impl EmitterWriter {
1590
1593
) ;
1591
1594
1592
1595
let mut row_num = 2 ;
1596
+ draw_col_separator_no_space ( & mut buffer, 1 , max_line_num_len + 1 ) ;
1593
1597
let mut notice_capitalization = false ;
1594
- for ( complete, parts, only_capitalization) in suggestions. iter ( ) . take ( MAX_SUGGESTIONS ) {
1598
+ for ( complete, parts, highlights, only_capitalization) in
1599
+ suggestions. iter ( ) . take ( MAX_SUGGESTIONS )
1600
+ {
1595
1601
notice_capitalization |= only_capitalization;
1596
1602
// Only show underline if the suggestion spans a single line and doesn't cover the
1597
1603
// entirety of the code output. If you have multiple replacements in the same line
1598
1604
// of code, show the underline.
1599
1605
let show_underline = !( parts. len ( ) == 1 && parts[ 0 ] . snippet . trim ( ) == complete. trim ( ) )
1600
1606
&& complete. lines ( ) . count ( ) == 1 ;
1601
1607
1602
- let lines = sm
1608
+ let has_deletion = parts. iter ( ) . any ( |p| p. is_deletion ( ) ) ;
1609
+ let is_multiline = complete. lines ( ) . count ( ) > 1 ;
1610
+
1611
+ let show_diff = has_deletion && !is_multiline;
1612
+
1613
+ if show_diff {
1614
+ row_num += 1 ;
1615
+ }
1616
+
1617
+ let file_lines = sm
1603
1618
. span_to_lines ( parts[ 0 ] . span )
1604
1619
. expect ( "span_to_lines failed when emitting suggestion" ) ;
1605
1620
1606
- assert ! ( !lines . lines. is_empty( ) || parts[ 0 ] . span. is_dummy( ) ) ;
1621
+ assert ! ( !file_lines . lines. is_empty( ) || parts[ 0 ] . span. is_dummy( ) ) ;
1607
1622
1608
1623
let line_start = sm. lookup_char_pos ( parts[ 0 ] . span . lo ( ) ) . line ;
1609
1624
draw_col_separator_no_space ( & mut buffer, 1 , max_line_num_len + 1 ) ;
1610
1625
let mut lines = complete. lines ( ) ;
1611
- for ( line_pos, line) in lines. by_ref ( ) . take ( MAX_SUGGESTION_HIGHLIGHT_LINES ) . enumerate ( )
1626
+ for ( line_pos, ( line, parts) ) in
1627
+ lines. by_ref ( ) . zip ( highlights) . take ( MAX_SUGGESTION_HIGHLIGHT_LINES ) . enumerate ( )
1612
1628
{
1613
1629
// Print the span column to avoid confusion
1614
1630
buffer. puts (
@@ -1617,9 +1633,60 @@ impl EmitterWriter {
1617
1633
& self . maybe_anonymized ( line_start + line_pos) ,
1618
1634
Style :: LineNumber ,
1619
1635
) ;
1636
+ if show_diff {
1637
+ // Add the line number for both addition and removal to drive the point home.
1638
+ //
1639
+ // N - fn foo<A: T>(bar: A) {
1640
+ // N + fn foo(bar: impl T) {
1641
+ buffer. puts (
1642
+ row_num - 1 ,
1643
+ 0 ,
1644
+ & self . maybe_anonymized ( line_start + line_pos) ,
1645
+ Style :: LineNumber ,
1646
+ ) ;
1647
+ buffer. puts ( row_num - 1 , max_line_num_len + 1 , "- " , Style :: Removal ) ;
1648
+ buffer. puts (
1649
+ row_num - 1 ,
1650
+ max_line_num_len + 3 ,
1651
+ & replace_tabs (
1652
+ & * file_lines
1653
+ . file
1654
+ . get_line ( file_lines. lines [ line_pos] . line_index )
1655
+ . unwrap ( ) ,
1656
+ ) ,
1657
+ Style :: NoStyle ,
1658
+ ) ;
1659
+ buffer. puts ( row_num, max_line_num_len + 1 , "+ " , Style :: Addition ) ;
1660
+ } else if is_multiline {
1661
+ match & parts[ ..] {
1662
+ [ SubstitutionHighlight { start : 0 , end } ] if * end == line. len ( ) => {
1663
+ buffer. puts ( row_num, max_line_num_len + 1 , "+ " , Style :: Addition ) ;
1664
+ }
1665
+ [ ] => {
1666
+ draw_col_separator ( & mut buffer, row_num, max_line_num_len + 1 ) ;
1667
+ }
1668
+ _ => {
1669
+ buffer. puts ( row_num, max_line_num_len + 1 , "~ " , Style :: Addition ) ;
1670
+ }
1671
+ }
1672
+ } else {
1673
+ draw_col_separator ( & mut buffer, row_num, max_line_num_len + 1 ) ;
1674
+ }
1675
+
1620
1676
// print the suggestion
1621
- draw_col_separator ( & mut buffer, row_num, max_line_num_len + 1 ) ;
1622
1677
buffer. append ( row_num, & replace_tabs ( line) , Style :: NoStyle ) ;
1678
+
1679
+ if is_multiline {
1680
+ for SubstitutionHighlight { start, end } in parts {
1681
+ buffer. set_style_range (
1682
+ row_num,
1683
+ max_line_num_len + 3 + start,
1684
+ max_line_num_len + 3 + end,
1685
+ Style :: Addition ,
1686
+ true ,
1687
+ ) ;
1688
+ }
1689
+ }
1623
1690
row_num += 1 ;
1624
1691
}
1625
1692
@@ -1654,25 +1721,36 @@ impl EmitterWriter {
1654
1721
let underline_start = ( span_start_pos + start) as isize + offset;
1655
1722
let underline_end = ( span_start_pos + start + sub_len) as isize + offset;
1656
1723
assert ! ( underline_start >= 0 && underline_end >= 0 ) ;
1724
+ let padding: usize = max_line_num_len + 3 ;
1657
1725
for p in underline_start..underline_end {
1658
- buffer. putc (
1659
- row_num,
1660
- ( ( max_line_num_len + 3 ) as isize + p) as usize ,
1661
- '^' ,
1662
- Style :: UnderlinePrimary ,
1726
+ // Colorize addition/replacements with green.
1727
+ buffer. set_style (
1728
+ row_num - 1 ,
1729
+ ( padding as isize + p) as usize ,
1730
+ Style :: Addition ,
1731
+ true ,
1663
1732
) ;
1664
- }
1665
- // underline removals too
1666
- if underline_start == underline_end {
1667
- for p in underline_start - 1 ..underline_start + 1 {
1733
+ if !show_diff {
1734
+ // If this is a replacement, underline with `^`, if this is an addition
1735
+ // underline with `+`.
1668
1736
buffer. putc (
1669
1737
row_num,
1670
- ( ( max_line_num_len + 3 ) as isize + p) as usize ,
1671
- '-' ,
1672
- Style :: UnderlineSecondary ,
1738
+ ( padding as isize + p) as usize ,
1739
+ if part . is_addition ( & sm ) { '+' } else { '~' } ,
1740
+ Style :: Addition ,
1673
1741
) ;
1674
1742
}
1675
1743
}
1744
+ if show_diff {
1745
+ // Colorize removal with red in diff format.
1746
+ buffer. set_style_range (
1747
+ row_num - 2 ,
1748
+ ( padding as isize + span_start_pos as isize ) as usize ,
1749
+ ( padding as isize + span_end_pos as isize ) as usize ,
1750
+ Style :: Removal ,
1751
+ true ,
1752
+ ) ;
1753
+ }
1676
1754
1677
1755
// length of the code after substitution
1678
1756
let full_sub_len = part
@@ -2129,6 +2207,12 @@ impl<'a> WritableDst<'a> {
2129
2207
fn apply_style ( & mut self , lvl : Level , style : Style ) -> io:: Result < ( ) > {
2130
2208
let mut spec = ColorSpec :: new ( ) ;
2131
2209
match style {
2210
+ Style :: Addition => {
2211
+ spec. set_fg ( Some ( Color :: Green ) ) . set_intense ( true ) ;
2212
+ }
2213
+ Style :: Removal => {
2214
+ spec. set_fg ( Some ( Color :: Red ) ) . set_intense ( true ) ;
2215
+ }
2132
2216
Style :: LineAndColumn => { }
2133
2217
Style :: LineNumber => {
2134
2218
spec. set_bold ( true ) ;
0 commit comments