Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/renderer/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ fn emit_suggestion_default(
is_first: bool,
is_cont: bool,
) {
let suggestions = sm.splice_lines(suggestion.markers.clone());
let suggestions = sm.splice_lines(suggestion.markers.clone(), suggestion.fold);

let buffer_offset = buffer.num_lines();
let mut row_num = buffer_offset + usize::from(!matches_previous_suggestion);
Expand Down Expand Up @@ -1511,8 +1511,12 @@ fn emit_suggestion_default(
}

let file_lines = sm.span_to_lines(parts[0].span.clone());
// We use the original span to get original line_start
let (line_start, line_end) = sm.span_to_locations(parts[0].original_span.clone());
let (line_start, line_end) = if suggestion.fold {
// We use the original span to get original line_start
sm.span_to_locations(parts[0].original_span.clone())
} else {
sm.span_to_locations(0..sm.source.len())
};
let mut lines = complete.lines();
if lines.clone().next().is_none() {
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
Expand Down Expand Up @@ -1545,7 +1549,7 @@ fn emit_suggestion_default(
last_pos = line_pos;

// Remember lines that are not highlighted to hide them if needed
if highlight_parts.is_empty() {
if highlight_parts.is_empty() && suggestion.fold {
unhighlighted_lines.push((line_pos, line));
continue;
}
Expand Down
32 changes: 24 additions & 8 deletions src/renderer/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ impl<'a> SourceMap<'a> {
pub(crate) fn splice_lines<'b>(
&'b self,
mut patches: Vec<Patch<'b>>,
fold: bool,
) -> Vec<(
String,
Vec<TrimmedPatch<'b>>,
Expand Down Expand Up @@ -430,11 +431,16 @@ impl<'a> SourceMap<'a> {
patches.sort_by_key(|p| p.span.start);

// Find the bounding span.
let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
return Vec::new();
};
let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
return Vec::new();
let (lo, hi) = if fold {
let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
return Vec::new();
};
let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
return Vec::new();
};
(lo, hi)
} else {
(0, source_len)
};

let lines = self.span_to_lines(lo..hi);
Expand Down Expand Up @@ -536,9 +542,19 @@ impl<'a> SourceMap<'a> {
}
}
highlights.push(std::mem::take(&mut line_highlight));
// if the replacement already ends with a newline, don't print the next line
if !buf.ends_with('\n') {
push_trailing(&mut buf, prev_line, &prev_hi, None);
if fold {
// if the replacement already ends with a newline, don't print the next line
if !buf.ends_with('\n') {
push_trailing(&mut buf, prev_line, &prev_hi, None);
}
} else {
// Add the trailing part of the source after the last patch
if let Some(snippet) = self.span_to_snippet(prev_hi.byte..source_len) {
buf.push_str(snippet);
for _ in snippet.matches('\n') {
highlights.push(std::mem::take(&mut line_highlight));
}
}
}
// remove trailing newlines
while buf.ends_with('\n') {
Expand Down
201 changes: 201 additions & 0 deletions tests/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4541,3 +4541,204 @@ help: add a `;` here
let renderer_unicode = renderer_ascii.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer_unicode.render(input), expected_unicode);
}

#[test]
fn suggestion_no_fold() {
let source = r#"fn main() {
let variable_name = 42;
function_with_lots_of_arguments(
variable_name,
variable_name,
variable_name,
variable_name,
);
}"#;
let path = "$DIR/trimmed_multiline_suggestion.rs";

let input = &[
Group::with_title(
Level::ERROR
.primary_title("this function takes 6 arguments but 5 arguments were supplied")
.id("E0061"),
)
.element(
Snippet::source(source)
.path(path)
.annotation(
AnnotationKind::Context
.span(108..121)
.label("argument #2 of type `char` is missing"),
)
.annotation(AnnotationKind::Primary.span(44..75)),
),
Group::with_title(Level::HELP.secondary_title("provide the argument")).element(
Snippet::source(source)
.path(path)
.fold(false)
.patch(Patch::new(
75..174,
"(
variable_name,
/* char */,
variable_name,
variable_name,
variable_name,
)",
)),
),
];

let expected_ascii = str![[r#"
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
--> $DIR/trimmed_multiline_suggestion.rs:3:5
|
3 | function_with_lots_of_arguments(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 | variable_name,
5 | variable_name,
| ------------- argument #2 of type `char` is missing
|
help: provide the argument
|
1 | fn main() {
2 | let variable_name = 42;
3 | function_with_lots_of_arguments(
4 | variable_name,
5 ~ /* char */,
6 ~ variable_name,
7 | variable_name,
8 | variable_name,
9 | );
10| }
|
"#]];
let renderer_ascii = Renderer::plain();
assert_data_eq!(renderer_ascii.render(input), expected_ascii);

let expected_unicode = str![[r#"
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
╭▸ $DIR/trimmed_multiline_suggestion.rs:3:5
3 │ function_with_lots_of_arguments(
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4 │ variable_name,
5 │ variable_name,
│ ───────────── argument #2 of type `char` is missing
╰╴
help: provide the argument
╭╴
1 │ fn main() {
2 │ let variable_name = 42;
3 │ function_with_lots_of_arguments(
4 │ variable_name,
5 ± /* char */,
6 ± variable_name,
7 │ variable_name,
8 │ variable_name,
9 │ );
10│ }
╰╴
"#]];
let renderer_unicode = renderer_ascii.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer_unicode.render(input), expected_unicode);
}

#[test]
fn suggestion_no_fold_replacement_ends_with_newline() {
let source = r#"
use st::cell::Cell;

mod bar {
pub fn bar() { bar::baz(); }

fn baz() {}
}

use bas::bar;

struct Foo {
bar: st::cell::Cell<bool>
}

fn main() {}"#;

let input = &[
Level::ERROR
.primary_title("failed to resolve: use of undeclared crate or module `st`")
.id("E0433")
.element(
Snippet::source(source).line_start(1).annotation(
AnnotationKind::Primary
.span(122..124)
.label("use of undeclared crate or module `st`"),
),
),
Level::HELP
.secondary_title("consider importing this module")
.element(
Snippet::source(source)
.fold(false)
.patch(Patch::new(1..1, "use std::cell;\n")),
),
];
let expected_ascii = str![[r#"
error[E0433]: failed to resolve: use of undeclared crate or module `st`
|
13 | bar: st::cell::Cell<bool>
| ^^ use of undeclared crate or module `st`
|
help: consider importing this module
|
1 |
2 + use std::cell;
3 ~ use st::cell::Cell;
4 |
5 | mod bar {
6 | pub fn bar() { bar::baz(); }
7 |
8 | fn baz() {}
9 | }
10 |
11 | use bas::bar;
12 |
13 | struct Foo {
14 | bar: st::cell::Cell<bool>
15 | }
16 |
17 | fn main() {}
|
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
error[E0433]: failed to resolve: use of undeclared crate or module `st`
╭▸
13 │ bar: st::cell::Cell<bool>
│ ━━ use of undeclared crate or module `st`
╰╴
help: consider importing this module
╭╴
1 │
2 + use std::cell;
3 ± use st::cell::Cell;
4 │
5 │ mod bar {
6 │ pub fn bar() { bar::baz(); }
7 │
8 │ fn baz() {}
9 │ }
10 │
11 │ use bas::bar;
12 │
13 │ struct Foo {
14 │ bar: st::cell::Cell<bool>
15 │ }
16 │
17 │ fn main() {}
╰╴
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}