Skip to content

Commit 3c3b623

Browse files
authored
Merge pull request #322 from Muscraft/fix-suggestion-folding
fix: Don't fold suggestions with fold = false
2 parents 7edacbb + ee6e975 commit 3c3b623

File tree

3 files changed

+233
-12
lines changed

3 files changed

+233
-12
lines changed

src/renderer/render.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ fn emit_suggestion_default(
14501450
is_first: bool,
14511451
is_cont: bool,
14521452
) {
1453-
let suggestions = sm.splice_lines(suggestion.markers.clone());
1453+
let suggestions = sm.splice_lines(suggestion.markers.clone(), suggestion.fold);
14541454

14551455
let buffer_offset = buffer.num_lines();
14561456
let mut row_num = buffer_offset + usize::from(!matches_previous_suggestion);
@@ -1511,8 +1511,12 @@ fn emit_suggestion_default(
15111511
}
15121512

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

15471551
// Remember lines that are not highlighted to hide them if needed
1548-
if highlight_parts.is_empty() {
1552+
if highlight_parts.is_empty() && suggestion.fold {
15491553
unhighlighted_lines.push((line_pos, line));
15501554
continue;
15511555
}

src/renderer/source_map.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ impl<'a> SourceMap<'a> {
370370
pub(crate) fn splice_lines<'b>(
371371
&'b self,
372372
mut patches: Vec<Patch<'b>>,
373+
fold: bool,
373374
) -> Vec<(
374375
String,
375376
Vec<TrimmedPatch<'b>>,
@@ -430,11 +431,16 @@ impl<'a> SourceMap<'a> {
430431
patches.sort_by_key(|p| p.span.start);
431432

432433
// Find the bounding span.
433-
let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
434-
return Vec::new();
435-
};
436-
let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
437-
return Vec::new();
434+
let (lo, hi) = if fold {
435+
let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
436+
return Vec::new();
437+
};
438+
let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
439+
return Vec::new();
440+
};
441+
(lo, hi)
442+
} else {
443+
(0, source_len)
438444
};
439445

440446
let lines = self.span_to_lines(lo..hi);
@@ -536,9 +542,19 @@ impl<'a> SourceMap<'a> {
536542
}
537543
}
538544
highlights.push(std::mem::take(&mut line_highlight));
539-
// if the replacement already ends with a newline, don't print the next line
540-
if !buf.ends_with('\n') {
541-
push_trailing(&mut buf, prev_line, &prev_hi, None);
545+
if fold {
546+
// if the replacement already ends with a newline, don't print the next line
547+
if !buf.ends_with('\n') {
548+
push_trailing(&mut buf, prev_line, &prev_hi, None);
549+
}
550+
} else {
551+
// Add the trailing part of the source after the last patch
552+
if let Some(snippet) = self.span_to_snippet(prev_hi.byte..source_len) {
553+
buf.push_str(snippet);
554+
for _ in snippet.matches('\n') {
555+
highlights.push(std::mem::take(&mut line_highlight));
556+
}
557+
}
542558
}
543559
// remove trailing newlines
544560
while buf.ends_with('\n') {

tests/formatter.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4541,3 +4541,204 @@ help: add a `;` here
45414541
let renderer_unicode = renderer_ascii.decor_style(DecorStyle::Unicode);
45424542
assert_data_eq!(renderer_unicode.render(input), expected_unicode);
45434543
}
4544+
4545+
#[test]
4546+
fn suggestion_no_fold() {
4547+
let source = r#"fn main() {
4548+
let variable_name = 42;
4549+
function_with_lots_of_arguments(
4550+
variable_name,
4551+
variable_name,
4552+
variable_name,
4553+
variable_name,
4554+
);
4555+
}"#;
4556+
let path = "$DIR/trimmed_multiline_suggestion.rs";
4557+
4558+
let input = &[
4559+
Group::with_title(
4560+
Level::ERROR
4561+
.primary_title("this function takes 6 arguments but 5 arguments were supplied")
4562+
.id("E0061"),
4563+
)
4564+
.element(
4565+
Snippet::source(source)
4566+
.path(path)
4567+
.annotation(
4568+
AnnotationKind::Context
4569+
.span(108..121)
4570+
.label("argument #2 of type `char` is missing"),
4571+
)
4572+
.annotation(AnnotationKind::Primary.span(44..75)),
4573+
),
4574+
Group::with_title(Level::HELP.secondary_title("provide the argument")).element(
4575+
Snippet::source(source)
4576+
.path(path)
4577+
.fold(false)
4578+
.patch(Patch::new(
4579+
75..174,
4580+
"(
4581+
variable_name,
4582+
/* char */,
4583+
variable_name,
4584+
variable_name,
4585+
variable_name,
4586+
)",
4587+
)),
4588+
),
4589+
];
4590+
4591+
let expected_ascii = str![[r#"
4592+
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
4593+
--> $DIR/trimmed_multiline_suggestion.rs:3:5
4594+
|
4595+
3 | function_with_lots_of_arguments(
4596+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4597+
4 | variable_name,
4598+
5 | variable_name,
4599+
| ------------- argument #2 of type `char` is missing
4600+
|
4601+
help: provide the argument
4602+
|
4603+
1 | fn main() {
4604+
2 | let variable_name = 42;
4605+
3 | function_with_lots_of_arguments(
4606+
4 | variable_name,
4607+
5 ~ /* char */,
4608+
6 ~ variable_name,
4609+
7 | variable_name,
4610+
8 | variable_name,
4611+
9 | );
4612+
10| }
4613+
|
4614+
"#]];
4615+
let renderer_ascii = Renderer::plain();
4616+
assert_data_eq!(renderer_ascii.render(input), expected_ascii);
4617+
4618+
let expected_unicode = str![[r#"
4619+
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
4620+
╭▸ $DIR/trimmed_multiline_suggestion.rs:3:5
4621+
4622+
3 │ function_with_lots_of_arguments(
4623+
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4624+
4 │ variable_name,
4625+
5 │ variable_name,
4626+
│ ───────────── argument #2 of type `char` is missing
4627+
╰╴
4628+
help: provide the argument
4629+
╭╴
4630+
1 │ fn main() {
4631+
2 │ let variable_name = 42;
4632+
3 │ function_with_lots_of_arguments(
4633+
4 │ variable_name,
4634+
5 ± /* char */,
4635+
6 ± variable_name,
4636+
7 │ variable_name,
4637+
8 │ variable_name,
4638+
9 │ );
4639+
10│ }
4640+
╰╴
4641+
"#]];
4642+
let renderer_unicode = renderer_ascii.decor_style(DecorStyle::Unicode);
4643+
assert_data_eq!(renderer_unicode.render(input), expected_unicode);
4644+
}
4645+
4646+
#[test]
4647+
fn suggestion_no_fold_replacement_ends_with_newline() {
4648+
let source = r#"
4649+
use st::cell::Cell;
4650+
4651+
mod bar {
4652+
pub fn bar() { bar::baz(); }
4653+
4654+
fn baz() {}
4655+
}
4656+
4657+
use bas::bar;
4658+
4659+
struct Foo {
4660+
bar: st::cell::Cell<bool>
4661+
}
4662+
4663+
fn main() {}"#;
4664+
4665+
let input = &[
4666+
Level::ERROR
4667+
.primary_title("failed to resolve: use of undeclared crate or module `st`")
4668+
.id("E0433")
4669+
.element(
4670+
Snippet::source(source).line_start(1).annotation(
4671+
AnnotationKind::Primary
4672+
.span(122..124)
4673+
.label("use of undeclared crate or module `st`"),
4674+
),
4675+
),
4676+
Level::HELP
4677+
.secondary_title("consider importing this module")
4678+
.element(
4679+
Snippet::source(source)
4680+
.fold(false)
4681+
.patch(Patch::new(1..1, "use std::cell;\n")),
4682+
),
4683+
];
4684+
let expected_ascii = str![[r#"
4685+
error[E0433]: failed to resolve: use of undeclared crate or module `st`
4686+
|
4687+
13 | bar: st::cell::Cell<bool>
4688+
| ^^ use of undeclared crate or module `st`
4689+
|
4690+
help: consider importing this module
4691+
|
4692+
1 |
4693+
2 + use std::cell;
4694+
3 ~ use st::cell::Cell;
4695+
4 |
4696+
5 | mod bar {
4697+
6 | pub fn bar() { bar::baz(); }
4698+
7 |
4699+
8 | fn baz() {}
4700+
9 | }
4701+
10 |
4702+
11 | use bas::bar;
4703+
12 |
4704+
13 | struct Foo {
4705+
14 | bar: st::cell::Cell<bool>
4706+
15 | }
4707+
16 |
4708+
17 | fn main() {}
4709+
|
4710+
"#]];
4711+
4712+
let renderer = Renderer::plain();
4713+
assert_data_eq!(renderer.render(input), expected_ascii);
4714+
4715+
let expected_unicode = str![[r#"
4716+
error[E0433]: failed to resolve: use of undeclared crate or module `st`
4717+
╭▸
4718+
13 │ bar: st::cell::Cell<bool>
4719+
│ ━━ use of undeclared crate or module `st`
4720+
╰╴
4721+
help: consider importing this module
4722+
╭╴
4723+
1 │
4724+
2 + use std::cell;
4725+
3 ± use st::cell::Cell;
4726+
4 │
4727+
5 │ mod bar {
4728+
6 │ pub fn bar() { bar::baz(); }
4729+
7 │
4730+
8 │ fn baz() {}
4731+
9 │ }
4732+
10 │
4733+
11 │ use bas::bar;
4734+
12 │
4735+
13 │ struct Foo {
4736+
14 │ bar: st::cell::Cell<bool>
4737+
15 │ }
4738+
16 │
4739+
17 │ fn main() {}
4740+
╰╴
4741+
"#]];
4742+
let renderer = renderer.decor_style(DecorStyle::Unicode);
4743+
assert_data_eq!(renderer.render(input), expected_unicode);
4744+
}

0 commit comments

Comments
 (0)