Improve suggestions when its parts are far from each other

Previously we only show at most 6 lines of suggestions and, if the
suggestions are more than 6 lines apart, we've just showed ... at the
end. This is probably fine, but quite confusing in my opinion.

This commit is an attempt to show ... in places where there is nothing
to suggest instead, for example:

Before:
```text
help: consider enclosing expression in a block
  |
3 ~     'l: { match () { () => break 'l,
4 |
5 |
6 |
7 |
8 |
...
```

After:
```text
help: consider enclosing expression in a block
  |
3 ~     'l: { match () { () => break 'l,
4 |
...
31|
32~ } };
  |
```
This commit is contained in:
Maybe Waffle 2022-06-06 18:04:42 +04:00
parent 260c5fd587
commit 87fded1edd

View File

@ -656,11 +656,6 @@ fn emit_diagnostic(&mut self, d: &Diagnostic) {
} }
} }
/// Maximum number of lines we will print for a multiline suggestion; arbitrary.
///
/// This should be replaced with a more involved mechanism to output multiline suggestions that
/// more closely mimics the regular diagnostic output, where irrelevant code lines are elided.
pub const MAX_SUGGESTION_HIGHLIGHT_LINES: usize = 6;
/// Maximum number of suggestions to be shown /// Maximum number of suggestions to be shown
/// ///
/// Arbitrary, but taken from trait import suggestion limit /// Arbitrary, but taken from trait import suggestion limit
@ -1839,79 +1834,115 @@ enum DisplaySuggestion {
} }
row_num += line_end - line_start; row_num += line_end - line_start;
} }
for (line_pos, (line, highlight_parts)) in let mut unhighlighted_lines = Vec::new();
lines.by_ref().zip(highlights).take(MAX_SUGGESTION_HIGHLIGHT_LINES).enumerate() for (line_pos, (line, highlight_parts)) in lines.by_ref().zip(highlights).enumerate() {
{ debug!(%line_pos, %line, ?highlight_parts);
// Print the span column to avoid confusion
buffer.puts( let print_line = |line_pos: usize,
row_num, line: &str,
0, highlight_parts: &Vec<SubstitutionHighlight>,
&self.maybe_anonymized(line_start + line_pos), buffer: &mut StyledBuffer,
Style::LineNumber, row_num: &mut usize| {
); // Print the span column to avoid confusion
if let DisplaySuggestion::Diff = show_code_change {
// Add the line number for both addition and removal to drive the point home.
//
// N - fn foo<A: T>(bar: A) {
// N + fn foo(bar: impl T) {
buffer.puts( buffer.puts(
row_num - 1, *row_num,
0, 0,
&self.maybe_anonymized(line_start + line_pos), &self.maybe_anonymized(line_start + line_pos),
Style::LineNumber, Style::LineNumber,
); );
buffer.puts(row_num - 1, max_line_num_len + 1, "- ", Style::Removal); if let DisplaySuggestion::Diff = show_code_change {
buffer.puts( // Add the line number for both addition and removal to drive the point home.
row_num - 1, //
max_line_num_len + 3, // N - fn foo<A: T>(bar: A) {
&normalize_whitespace( // N + fn foo(bar: impl T) {
&*file_lines buffer.puts(
.file *row_num - 1,
.get_line(file_lines.lines[line_pos].line_index) 0,
.unwrap(), &self.maybe_anonymized(line_start + line_pos),
), Style::LineNumber,
Style::NoStyle, );
); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.puts(
} else if is_multiline { *row_num - 1,
match &highlight_parts[..] { max_line_num_len + 3,
[SubstitutionHighlight { start: 0, end }] if *end == line.len() => { &normalize_whitespace(
buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); &*file_lines
} .file
[] => { .get_line(file_lines.lines[line_pos].line_index)
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); .unwrap(),
} ),
_ => { Style::NoStyle,
buffer.puts(row_num, max_line_num_len + 1, "~ ", Style::Addition); );
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
} else if is_multiline {
match &highlight_parts[..] {
[SubstitutionHighlight { start: 0, end }] if *end == line.len() => {
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
}
[] => {
draw_col_separator(buffer, *row_num, max_line_num_len + 1);
}
_ => {
buffer.puts(*row_num, max_line_num_len + 1, "~ ", Style::Addition);
}
} }
} else {
draw_col_separator(buffer, *row_num, max_line_num_len + 1);
} }
} else {
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); // print the suggestion
buffer.append(*row_num, &normalize_whitespace(line), Style::NoStyle);
// Colorize addition/replacements with green.
for &SubstitutionHighlight { start, end } in highlight_parts {
// Account for tabs when highlighting (#87972).
let tabs: usize = line
.chars()
.take(start)
.map(|ch| match ch {
'\t' => 3,
_ => 0,
})
.sum();
buffer.set_style_range(
*row_num,
max_line_num_len + 3 + start + tabs,
max_line_num_len + 3 + end + tabs,
Style::Addition,
true,
);
}
*row_num += 1;
};
if highlight_parts.is_empty() {
unhighlighted_lines.push((line_pos, line));
continue;
} }
// print the suggestion match unhighlighted_lines.len() {
buffer.append(row_num, &normalize_whitespace(line), Style::NoStyle); 0 => (),
// Since we show first line, "..." line and last line,
// Colorize addition/replacements with green. // There is no reason to hide if there are 3 or less lines
for &SubstitutionHighlight { start, end } in highlight_parts { // (because then we just replace a line with ... which is
// Account for tabs when highlighting (#87972). // not helpful)
let tabs: usize = line n if n <= 3 => unhighlighted_lines.drain(..).for_each(|(p, l)| {
.chars() print_line(p, l, &Vec::new(), &mut buffer, &mut row_num)
.take(start) }),
.map(|ch| match ch { _ => {
'\t' => 3, unhighlighted_lines
_ => 0, .drain(..1)
}) .next()
.sum(); .map(|(p, l)| print_line(p, l, &Vec::new(), &mut buffer, &mut row_num));
buffer.set_style_range( buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber);
row_num, row_num += 1;
max_line_num_len + 3 + start + tabs, unhighlighted_lines
max_line_num_len + 3 + end + tabs, .pop()
Style::Addition, .map(|(p, l)| print_line(p, l, &Vec::new(), &mut buffer, &mut row_num));
true, }
);
} }
row_num += 1;
print_line(line_pos, line, highlight_parts, &mut buffer, &mut row_num)
} }
// This offset and the ones below need to be signed to account for replacement code // This offset and the ones below need to be signed to account for replacement code