Skip to content

Commit 686ed13

Browse files
committed
unwrap anonymous functions with comments in
The indentation of comments when we're done with them might be a bit off, but it's nothing an autoformat shouldn't be able to fix
1 parent d2d9833 commit 686ed13

7 files changed

+212
-28
lines changed

compiler-core/src/language_server/code_action.rs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7626,7 +7626,8 @@ impl<'ast> ast::visit::Visit<'ast> for WrapInAnonymousFunction<'ast> {
76267626
}
76277627
}
76287628

7629-
/// Code action to unwrap trivial one-statement anonymous functions into just a reference to the function called
7629+
/// Code action to unwrap trivial one-statement anonymous functions into just a
7630+
/// reference to the function called
76307631
///
76317632
/// For example, if the code action was used on the anonymous function here:
76327633
///
@@ -7651,9 +7652,10 @@ pub struct UnwrapAnonymousFunction<'a> {
76517652
/// Helper struct, a target for [UnwrapAnonymousFunction]
76527653
struct FunctionToUnwrap {
76537654
/// Location of the anonymous function to apply the action to
7654-
anonymous_function_location: SrcSpan,
7655-
/// Location of the function being called inside the anonymous function. This will be all that's left after the action.
7656-
inner_function_location: SrcSpan,
7655+
outer_function: SrcSpan,
7656+
/// Location of the function being called inside the anonymous function.
7657+
/// This will be all that's left after the action, plus any comments.
7658+
inner_function: SrcSpan,
76577659
}
76587660

76597661
impl<'a> UnwrapAnonymousFunction<'a> {
@@ -7674,16 +7676,29 @@ impl<'a> UnwrapAnonymousFunction<'a> {
76747676
self.visit_typed_module(&self.module.ast);
76757677

76767678
let mut actions = Vec::with_capacity(self.functions.len());
7677-
for target in self.functions {
7679+
for function in &self.functions {
76787680
let mut edits = TextEdits::new(self.line_numbers);
76797681

7682+
// We need to delete the anonymous function's head but preserve
7683+
// comments between it and the inner function call.
7684+
edits.delete(self.span_until_comment(SrcSpan {
7685+
start: function.outer_function.start,
7686+
end: function.inner_function.start,
7687+
}));
7688+
7689+
// Now we need to delete the inner function call's arguments,
7690+
// preserving comments before the outer function tail.
7691+
edits.delete(self.span_until_comment(SrcSpan {
7692+
start: function.inner_function.end,
7693+
end: function.outer_function.end - 1,
7694+
}));
7695+
7696+
// To delete the tail we nip one character to make sure we get the
7697+
// `}`. This could be redundant with the above if there were no
7698+
// comments, but that's fine.
76807699
edits.delete(SrcSpan {
7681-
start: target.anonymous_function_location.start,
7682-
end: target.inner_function_location.start,
7683-
});
7684-
edits.delete(SrcSpan {
7685-
start: target.inner_function_location.end,
7686-
end: target.anonymous_function_location.end,
7700+
start: function.outer_function.end - 1,
7701+
end: function.outer_function.end,
76877702
});
76887703

76897704
CodeActionBuilder::new("Remove anonymous function wrapper")
@@ -7694,6 +7709,24 @@ impl<'a> UnwrapAnonymousFunction<'a> {
76947709
actions
76957710
}
76967711

7712+
// Returns the given span, but with the end point adjusted to the start
7713+
// of the first comment in the span, if any.
7714+
fn span_until_comment(&self, span: SrcSpan) -> SrcSpan {
7715+
let SrcSpan { start, end } = span;
7716+
let adjusted_end = self
7717+
.module
7718+
.extra
7719+
.first_comment_between(start, end)
7720+
// The above will return the start of the comment's text, so we need
7721+
// to step backwards a bit to get the `//`.
7722+
.map(|comment| comment.start - 2)
7723+
.unwrap_or(end);
7724+
SrcSpan {
7725+
start,
7726+
end: adjusted_end,
7727+
}
7728+
}
7729+
76977730
/// If an anonymous function can be unwrapped, save it to our list
76987731
///
76997732
/// We need to ensure our subjects:
@@ -7714,15 +7747,6 @@ impl<'a> UnwrapAnonymousFunction<'a> {
77147747
_ => return,
77157748
}
77167749

7717-
// We can't apply to functions with comments in (yet)
7718-
if self
7719-
.module
7720-
.extra
7721-
.has_comment_between(location.start, location.end)
7722-
{
7723-
return;
7724-
}
7725-
77267750
// We can only apply to anonymous functions containing a single function call
77277751
let [
77287752
TypedStatement::Expression(TypedExpr::Call {
@@ -7761,8 +7785,8 @@ impl<'a> UnwrapAnonymousFunction<'a> {
77617785
}
77627786

77637787
self.functions.push(FunctionToUnwrap {
7764-
anonymous_function_location: *location,
7765-
inner_function_location: called_function.location(),
7788+
outer_function: *location,
7789+
inner_function: called_function.location(),
77667790
})
77677791
}
77687792
}

compiler-core/src/language_server/tests/action.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9412,8 +9412,8 @@ fn op(first a, second b) {
94129412
}
94139413

94149414
#[test]
9415-
fn dont_unwrap_anonymous_function_with_comment_after() {
9416-
assert_no_code_actions!(
9415+
fn unwrap_anonymous_function_with_comment_after() {
9416+
assert_code_action!(
94179417
UNWRAP_ANONYMOUS_FUNCTION,
94189418
"pub fn main() {
94199419
fn(a) {
@@ -9431,12 +9431,54 @@ fn op(a) {
94319431
}
94329432

94339433
#[test]
9434-
fn dont_unwrap_anonymous_function_with_comment_before() {
9435-
assert_no_code_actions!(
9434+
fn unwrap_anonymous_function_with_comment_on_line() {
9435+
assert_code_action!(
94369436
UNWRAP_ANONYMOUS_FUNCTION,
94379437
"pub fn main() {
94389438
fn(a) {
9439-
// look out!
9439+
op(a) // look out!
9440+
}
9441+
}
9442+
9443+
fn op(a) {
9444+
todo
9445+
}
9446+
",
9447+
find_position_of("fn(a)").to_selection()
9448+
);
9449+
}
9450+
9451+
#[test]
9452+
fn unwrap_anonymous_function_with_comment_on_head_line() {
9453+
assert_code_action!(
9454+
UNWRAP_ANONYMOUS_FUNCTION,
9455+
"pub fn main() {
9456+
fn(a) { // look out!
9457+
op(a)
9458+
}
9459+
}
9460+
9461+
fn op(a) {
9462+
todo
9463+
}
9464+
",
9465+
find_position_of("fn(a)").to_selection()
9466+
);
9467+
}
9468+
9469+
#[test]
9470+
fn unwrap_anonymous_function_with_comments_before() {
9471+
assert_code_action!(
9472+
UNWRAP_ANONYMOUS_FUNCTION,
9473+
"pub fn main() {
9474+
fn(a) {
9475+
// look out,
9476+
// there's a comment!
9477+
9478+
// another comment!
9479+
//here's one without a leading space
9480+
// here's one indented wrong
9481+
// here's one indented even wronger
94409482
op(a)
94419483
}
94429484
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "pub fn main() {\n fn(a) {\n op(a)\n // look out!\n }\n}\n\nfn op(a) {\n todo\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
pub fn main() {
7+
fn(a) {
8+
9+
op(a)
10+
// look out!
11+
}
12+
}
13+
14+
fn op(a) {
15+
todo
16+
}
17+
18+
19+
----- AFTER ACTION
20+
pub fn main() {
21+
op// look out!
22+
23+
}
24+
25+
fn op(a) {
26+
todo
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "pub fn main() {\n fn(a) { // look out!\n op(a)\n }\n}\n\nfn op(a) {\n todo\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
pub fn main() {
7+
fn(a) { // look out!
8+
9+
op(a)
10+
}
11+
}
12+
13+
fn op(a) {
14+
todo
15+
}
16+
17+
18+
----- AFTER ACTION
19+
pub fn main() {
20+
// look out!
21+
op
22+
}
23+
24+
fn op(a) {
25+
todo
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "pub fn main() {\n fn(a) {\n op(a) // look out!\n }\n}\n\nfn op(a) {\n todo\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
pub fn main() {
7+
fn(a) {
8+
9+
op(a) // look out!
10+
}
11+
}
12+
13+
fn op(a) {
14+
todo
15+
}
16+
17+
18+
----- AFTER ACTION
19+
pub fn main() {
20+
op// look out!
21+
22+
}
23+
24+
fn op(a) {
25+
todo
26+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "pub fn main() {\n fn(a) {\n // look out,\n // there's a comment!\n\n // another comment!\n //here's one without a leading space\n // here's one indented wrong\n // here's one indented even wronger\n op(a)\n }\n}\n\nfn op(a) {\n todo\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
pub fn main() {
7+
fn(a) {
8+
9+
// look out,
10+
// there's a comment!
11+
12+
// another comment!
13+
//here's one without a leading space
14+
// here's one indented wrong
15+
// here's one indented even wronger
16+
op(a)
17+
}
18+
}
19+
20+
fn op(a) {
21+
todo
22+
}
23+
24+
25+
----- AFTER ACTION
26+
pub fn main() {
27+
// look out,
28+
// there's a comment!
29+
30+
// another comment!
31+
//here's one without a leading space
32+
// here's one indented wrong
33+
// here's one indented even wronger
34+
op
35+
}
36+
37+
fn op(a) {
38+
todo
39+
}

compiler-core/src/parse/extra.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl ModuleExtra {
6565
let mut search_list = &self.comments[..];
6666
while let Some(index) = inner(search_list, start, end) {
6767
best = self.comments.get(index).copied();
68-
search_list = &search_list[0..index];
68+
search_list = search_list.get(0..index).unwrap_or(&[]);
6969
}
7070
best
7171
}

0 commit comments

Comments
 (0)