Skip to content

Commit a0c331f

Browse files
committed
get fn locations from parser
This increases the blast radius of the change a bit, but means we don't need to worry about comments any more.
1 parent ebeda16 commit a0c331f

13 files changed

+122
-63
lines changed

compiler-core/src/ast/typed.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub enum TypedExpr {
7676
type_: Arc<Type>,
7777
fun: Box<Self>,
7878
arguments: Vec<CallArg<Self>>,
79+
arguments_start: Option<u32>,
7980
},
8081

8182
BinOp {

compiler-core/src/ast/untyped.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub enum UntypedExpr {
5353
location: SrcSpan,
5454
fun: Box<Self>,
5555
arguments: Vec<CallArg<Self>>,
56+
arguments_start: u32,
5657
},
5758

5859
BinOp {
@@ -293,7 +294,7 @@ impl HasLocation for UntypedExpr {
293294
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294295
pub enum FunctionLiteralKind {
295296
Capture { hole: SrcSpan },
296-
Anonymous { head: SrcSpan },
297+
Anonymous { head: SrcSpan, body: SrcSpan },
297298
Use { location: SrcSpan },
298299
}
299300

compiler-core/src/ast/visit.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,9 @@ pub trait Visit<'ast> {
196196
type_: &'ast Arc<Type>,
197197
fun: &'ast TypedExpr,
198198
arguments: &'ast [TypedCallArg],
199+
arguments_start: &'ast Option<u32>,
199200
) {
200-
visit_typed_expr_call(self, location, type_, fun, arguments);
201+
visit_typed_expr_call(self, location, type_, fun, arguments, arguments_start);
201202
}
202203

203204
fn visit_typed_expr_bin_op(
@@ -790,7 +791,8 @@ where
790791
type_,
791792
fun,
792793
arguments,
793-
} => v.visit_typed_expr_call(location, type_, fun, arguments),
794+
arguments_start,
795+
} => v.visit_typed_expr_call(location, type_, fun, arguments, arguments_start),
794796
TypedExpr::BinOp {
795797
location,
796798
type_,
@@ -1003,6 +1005,7 @@ pub fn visit_typed_expr_call<'a, V>(
10031005
_type_: &'a Arc<Type>,
10041006
fun: &'a TypedExpr,
10051007
arguments: &'a [TypedCallArg],
1008+
_arguments_start: &'a Option<u32>,
10061009
) where
10071010
V: Visit<'a> + ?Sized,
10081011
{

compiler-core/src/ast_folder.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold
289289
location,
290290
fun,
291291
arguments,
292-
} => self.fold_call(location, fun, arguments),
292+
arguments_start,
293+
} => self.fold_call(location, fun, arguments, arguments_start),
293294

294295
UntypedExpr::BinOp {
295296
location,
@@ -445,6 +446,7 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold
445446
location,
446447
fun,
447448
arguments,
449+
arguments_start,
448450
} => {
449451
let fun = Box::new(self.fold_expr(*fun));
450452
let arguments = arguments
@@ -458,6 +460,7 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold
458460
location,
459461
fun,
460462
arguments,
463+
arguments_start,
461464
}
462465
}
463466

@@ -770,11 +773,13 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold
770773
location: SrcSpan,
771774
fun: Box<UntypedExpr>,
772775
arguments: Vec<CallArg<UntypedExpr>>,
776+
arguments_start: u32,
773777
) -> UntypedExpr {
774778
UntypedExpr::Call {
775779
location,
776780
fun,
777781
arguments,
782+
arguments_start,
778783
}
779784
}
780785

compiler-core/src/format.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,7 @@ impl<'comments> Formatter<'comments> {
15931593
fun,
15941594
arguments,
15951595
location,
1596+
..
15961597
})) = call.first()
15971598
else {
15981599
// The body of a capture being not a fn shouldn't be possible...

compiler-core/src/language_server/code_action.rs

Lines changed: 80 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,28 @@ fn count_indentation(code: &str, line_numbers: &LineNumbers, line: u32) -> usize
102102
indent_size
103103
}
104104

105+
// Given a string and a position in it, if the position points to whitespace,
106+
// this function returns the next position which doesn't.
107+
fn next_nonwhitespace(string: &EcoString, position: u32) -> u32 {
108+
let mut n = position;
109+
let mut chars = string[position as usize..].chars();
110+
while chars.next().is_some_and(char::is_whitespace) {
111+
n += 1;
112+
}
113+
n
114+
}
115+
116+
// Given a string and a position in it, if the position points after whitespace,
117+
// this function returns the previous position which doesn't.
118+
fn previous_nonwhitespace(string: &EcoString, position: u32) -> u32 {
119+
let mut n = position;
120+
let mut chars = string[..position as usize].chars();
121+
while chars.next_back().is_some_and(char::is_whitespace) {
122+
n -= 1;
123+
}
124+
n
125+
}
126+
105127
/// Code action to remove literal tuples in case subjects, essentially making
106128
/// the elements of the tuples into the case's subjects.
107129
///
@@ -842,6 +864,7 @@ impl<'ast> ast::visit::Visit<'ast> for FillInMissingLabelledArgs<'ast> {
842864
type_: &'ast Arc<Type>,
843865
fun: &'ast TypedExpr,
844866
arguments: &'ast [TypedCallArg],
867+
arguments_start: &'ast Option<u32>,
845868
) {
846869
let call_range = self.edits.src_span_to_lsp_range(*location);
847870
if !within(self.params.range, call_range) {
@@ -864,7 +887,7 @@ impl<'ast> ast::visit::Visit<'ast> for FillInMissingLabelledArgs<'ast> {
864887
// we're inside a nested call.
865888
let previous = self.use_right_hand_side_location;
866889
self.use_right_hand_side_location = None;
867-
ast::visit::visit_typed_expr_call(self, location, type_, fun, arguments);
890+
ast::visit::visit_typed_expr_call(self, location, type_, fun, arguments, arguments_start);
868891
self.use_right_hand_side_location = previous;
869892
}
870893

@@ -1250,7 +1273,7 @@ impl<'ast> ast::visit::Visit<'ast> for AddAnnotations<'_> {
12501273
let location = match kind {
12511274
// Function captures don't need any type annotations
12521275
FunctionLiteralKind::Capture { .. } => return,
1253-
FunctionLiteralKind::Anonymous { head } => head,
1276+
FunctionLiteralKind::Anonymous { head, .. } => head,
12541277
FunctionLiteralKind::Use { location } => location,
12551278
};
12561279

@@ -5172,6 +5195,7 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateFunction<'ast> {
51725195
type_: &'ast Arc<Type>,
51735196
fun: &'ast TypedExpr,
51745197
arguments: &'ast [TypedCallArg],
5198+
arguments_start: &'ast Option<u32>,
51755199
) {
51765200
// If the function being called is invalid we need to generate a
51775201
// function that has the proper labels.
@@ -5182,7 +5206,14 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateFunction<'ast> {
51825206
self.try_save_function_to_generate(fun.location(), &fun.type_(), Some(arguments));
51835207
}
51845208
} else {
5185-
ast::visit::visit_typed_expr_call(self, location, type_, fun, arguments);
5209+
ast::visit::visit_typed_expr_call(
5210+
self,
5211+
location,
5212+
type_,
5213+
fun,
5214+
arguments,
5215+
arguments_start,
5216+
);
51865217
}
51875218
}
51885219
}
@@ -5502,6 +5533,7 @@ where
55025533
type_: &'ast Arc<Type>,
55035534
fun: &'ast TypedExpr,
55045535
arguments: &'ast [TypedCallArg],
5536+
arguments_start: &'ast Option<u32>,
55055537
) {
55065538
// If the function being called is invalid we need to generate a
55075539
// function that has the proper labels.
@@ -5515,7 +5547,14 @@ where
55155547
);
55165548
}
55175549
} else {
5518-
ast::visit::visit_typed_expr_call(self, location, type_, fun, arguments);
5550+
ast::visit::visit_typed_expr_call(
5551+
self,
5552+
location,
5553+
type_,
5554+
fun,
5555+
arguments,
5556+
arguments_start,
5557+
);
55195558
}
55205559
}
55215560

@@ -5973,12 +6012,7 @@ impl<'a> InlineVariable<'a> {
59736012
}
59746013

59756014
let mut location = assignment.location;
5976-
5977-
let mut chars = self.module.code[location.end as usize..].chars();
5978-
// Delete any whitespace after the removed statement
5979-
while chars.next().is_some_and(char::is_whitespace) {
5980-
location.end += 1;
5981-
}
6015+
location.end = next_nonwhitespace(&self.module.code, location.end);
59826016

59836017
self.edits.delete(location);
59846018

@@ -6205,6 +6239,7 @@ impl<'ast> ast::visit::Visit<'ast> for ConvertToPipe<'ast> {
62056239
_type_: &'ast Arc<Type>,
62066240
fun: &'ast TypedExpr,
62076241
arguments: &'ast [TypedCallArg],
6242+
_arguments_start: &'ast Option<u32>,
62086243
) {
62096244
if arguments.iter().any(|arg| arg.is_capture_hole()) {
62106245
return;
@@ -7616,6 +7651,7 @@ impl<'ast> ast::visit::Visit<'ast> for WrapInAnonymousFunction<'ast> {
76167651
_type: &'ast Arc<Type>,
76177652
fun: &'ast TypedExpr,
76187653
arguments: &'ast [TypedCallArg],
7654+
_arguments_start: &'ast Option<u32>,
76197655
) {
76207656
// We only need to do this interception for explicit calls, so if any
76217657
// of our arguments are explicit we re-enter the visitor as usual.
@@ -7658,11 +7694,15 @@ pub struct UnwrapAnonymousFunction<'a> {
76587694

76597695
/// Helper struct, a target for [UnwrapAnonymousFunction]
76607696
struct FunctionToUnwrap {
7661-
/// Location of the anonymous function to apply the action to
7697+
/// Location of the anonymous function to apply the action to.
76627698
outer_function: SrcSpan,
7699+
/// Location of the opening brace of the anonymous function.
7700+
outer_function_body_start: u32,
76637701
/// Location of the function being called inside the anonymous function.
76647702
/// This will be all that's left after the action, plus any comments.
76657703
inner_function: SrcSpan,
7704+
// Location of the opening parenthesis of the inner function's argument list.
7705+
inner_function_arguments_start: u32,
76667706
}
76677707

76687708
impl<'a> UnwrapAnonymousFunction<'a> {
@@ -7686,25 +7726,29 @@ impl<'a> UnwrapAnonymousFunction<'a> {
76867726
for function in &self.functions {
76877727
let mut edits = TextEdits::new(self.line_numbers);
76887728

7689-
// We need to delete the anonymous function's head but preserve
7690-
// comments between it and the inner function call.
7691-
edits.delete(self.span_until_comment(SrcSpan {
7729+
// We need to delete the anonymous function's head and the opening
7730+
// brace but preserve comments between it and the inner function call.
7731+
// We set our endpoint at the start of the function body, and move
7732+
// it on through any whitespace.
7733+
let head_deletion_end =
7734+
next_nonwhitespace(&self.module.code, function.outer_function_body_start + 1);
7735+
edits.delete(SrcSpan {
76927736
start: function.outer_function.start,
7693-
end: function.inner_function.start,
7694-
}));
7695-
7696-
// Now we need to delete the inner function call's arguments,
7697-
// preserving comments before the outer function tail.
7698-
edits.delete(self.span_until_comment(SrcSpan {
7699-
start: function.inner_function.end,
7700-
end: function.outer_function.end - 1,
7701-
}));
7702-
7703-
// To delete the tail we nip one character to make sure we get the
7704-
// `}`. This could be redundant with the above if there were no
7705-
// comments, but that's fine.
7737+
end: head_deletion_end,
7738+
});
7739+
7740+
// Delete the inner function call's arguments.
77067741
edits.delete(SrcSpan {
7707-
start: function.outer_function.end - 1,
7742+
start: function.inner_function_arguments_start,
7743+
end: function.inner_function.end,
7744+
});
7745+
7746+
// To delete the tail we remove the function end (the '}') and any
7747+
// whitespace before it.
7748+
let tail_deletion_start =
7749+
previous_nonwhitespace(&self.module.code, function.outer_function.end - 1);
7750+
edits.delete(SrcSpan {
7751+
start: tail_deletion_start,
77087752
end: function.outer_function.end,
77097753
});
77107754

@@ -7716,24 +7760,6 @@ impl<'a> UnwrapAnonymousFunction<'a> {
77167760
actions
77177761
}
77187762

7719-
// Returns the given span, but with the end point adjusted to the start
7720-
// of the first comment in the span, if any.
7721-
fn span_until_comment(&self, span: SrcSpan) -> SrcSpan {
7722-
let SrcSpan { start, end } = span;
7723-
let adjusted_end = self
7724-
.module
7725-
.extra
7726-
.first_comment_between(start, end)
7727-
// The above will return the start of the comment's text, so we need
7728-
// to step backwards a bit to get the `//`.
7729-
.map(|comment| comment.start - 2)
7730-
.unwrap_or(end);
7731-
SrcSpan {
7732-
start,
7733-
end: adjusted_end,
7734-
}
7735-
}
7736-
77377763
/// If an anonymous function can be unwrapped, save it to our list
77387764
///
77397765
/// We need to ensure our subjects:
@@ -7749,16 +7775,17 @@ impl<'a> UnwrapAnonymousFunction<'a> {
77497775
arguments: &'a [TypedArg],
77507776
body: &'a Vec1<TypedStatement>,
77517777
) {
7752-
match kind {
7753-
FunctionLiteralKind::Anonymous { .. } => (),
7778+
let outer_body = match kind {
7779+
FunctionLiteralKind::Anonymous { body, .. } => body,
77547780
_ => return,
7755-
}
7781+
};
77567782

77577783
// We can only apply to anonymous functions containing a single function call
77587784
let [
77597785
TypedStatement::Expression(TypedExpr::Call {
7760-
fun: called_function,
7786+
location: call_location,
77617787
arguments: call_arguments,
7788+
arguments_start: Some(arguments_start),
77627789
..
77637790
}),
77647791
] = body.as_slice()
@@ -7793,7 +7820,9 @@ impl<'a> UnwrapAnonymousFunction<'a> {
77937820

77947821
self.functions.push(FunctionToUnwrap {
77957822
outer_function: *location,
7796-
inner_function: called_function.location(),
7823+
outer_function_body_start: outer_body.start,
7824+
inner_function: *call_location,
7825+
inner_function_arguments_start: *arguments_start,
77977826
})
77987827
}
77997828
}

compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__unwrap_anonymous_function_with_comment_after.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ fn op(a) {
1818

1919
----- AFTER ACTION
2020
pub fn main() {
21-
op// look out!
22-
21+
op
22+
// look out!
2323
}
2424

2525
fn op(a) {

compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__unwrap_anonymous_function_with_comment_on_line.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ fn op(a) {
1717

1818
----- AFTER ACTION
1919
pub fn main() {
20-
op// look out!
21-
20+
op // look out!
2221
}
2322

2423
fn op(a) {

0 commit comments

Comments
 (0)