+31
CHANGELOG.md
+31
CHANGELOG.md
···
160
160
161
161
### Language server
162
162
163
+
- It is now possible to use the "Pattern match on variable" code action on
164
+
variables on the left hand side of a `use`. For example:
165
+
166
+
```gleam
167
+
pub type User {
168
+
User(id: Int, name: String)
169
+
}
170
+
171
+
pub fn main() {
172
+
use user <- result.try(load_user())
173
+
// ^^^^ Triggering the code action here
174
+
todo
175
+
}
176
+
```
177
+
178
+
Would result in the following code:
179
+
180
+
```gleam
181
+
pub type User {
182
+
User(id: Int, name: String)
183
+
}
184
+
185
+
pub fn main() {
186
+
use user <- result.try(load_user())
187
+
let User(id:, name:) = user
188
+
todo
189
+
}
190
+
```
191
+
192
+
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
193
+
163
194
### Formatter
164
195
165
196
- The formatter now allows more control over how lists are split. By adding a
+12
compiler-core/src/ast.rs
+12
compiler-core/src/ast.rs
···
2982
2982
}
2983
2983
self.call.find_node(byte_index)
2984
2984
}
2985
+
2986
+
pub fn callback_arguments<'a>(&'a self) -> Option<&'a Vec<TypedArg>> {
2987
+
let TypedExpr::Call { args, .. } = self.call.as_ref() else {
2988
+
return None;
2989
+
};
2990
+
let callback = args.iter().last()?;
2991
+
let TypedExpr::Fn { args, .. } = &callback.value else {
2992
+
// The expression might be invalid so we have to return a None here
2993
+
return None;
2994
+
};
2995
+
Some(args)
2996
+
}
2985
2997
}
2986
2998
2987
2999
pub type TypedStatement = Statement<Arc<Type>, TypedExpr>;
+53
-3
compiler-core/src/language_server/code_action.rs
+53
-3
compiler-core/src/language_server/code_action.rs
···
3
3
use crate::{
4
4
Error, STDLIB_PACKAGE_NAME, analyse,
5
5
ast::{
6
-
self, AssignName, AssignmentKind, BitArraySegmentTruncation, CallArg, CustomType,
6
+
self, ArgNames, AssignName, AssignmentKind, BitArraySegmentTruncation, CallArg, CustomType,
7
7
FunctionLiteralKind, ImplicitCallArgOrigin, Import, PIPE_PRECEDENCE, Pattern,
8
8
PatternUnusedArguments, PipelineAssignmentKind, RecordConstructor, SrcSpan, TodoKind,
9
9
TypedArg, TypedAssignment, TypedExpr, TypedModuleConstant, TypedPattern,
···
4498
4498
///
4499
4499
assignment_location: SrcSpan,
4500
4500
},
4501
+
UseVariable {
4502
+
variable_name: &'a EcoString,
4503
+
variable_type: &'a Arc<Type>,
4504
+
/// The location of the entire use expression the variable is part of,
4505
+
/// so that we can add the pattern matching _after_ it.
4506
+
///
4507
+
use_location: SrcSpan,
4508
+
},
4501
4509
}
4502
4510
4503
4511
impl<'a, IO> PatternMatchOnValue<'a, IO>
···
4534
4542
Some(PatternMatchedValue::LetVariable {
4535
4543
variable_name,
4536
4544
variable_type,
4537
-
assignment_location,
4545
+
assignment_location: location,
4546
+
})
4547
+
| Some(PatternMatchedValue::UseVariable {
4548
+
variable_name,
4549
+
variable_type,
4550
+
use_location: location,
4538
4551
}) => {
4539
-
self.match_on_let_variable(variable_name, variable_type, assignment_location);
4552
+
self.match_on_let_variable(variable_name, variable_type, location);
4540
4553
"Pattern match on variable"
4541
4554
}
4542
4555
None => return vec![],
···
4877
4890
}
4878
4891
4879
4892
ast::visit::visit_typed_assignment(self, assignment);
4893
+
}
4894
+
4895
+
fn visit_typed_use(&mut self, use_: &'ast TypedUse) {
4896
+
if let Some(assignments) = use_.callback_arguments() {
4897
+
for variable in assignments {
4898
+
let ast::Arg {
4899
+
names: ArgNames::Named { name, .. },
4900
+
location: variable_location,
4901
+
type_,
4902
+
..
4903
+
} = variable
4904
+
else {
4905
+
continue;
4906
+
};
4907
+
4908
+
// If we use a pattern in a use assignment, that will end up
4909
+
// being called `_use` something. We don't want to offer the
4910
+
// action when hovering a pattern so we ignore those.
4911
+
if name.starts_with("_use") {
4912
+
continue;
4913
+
}
4914
+
4915
+
let variable_range = self.edits.src_span_to_lsp_range(*variable_location);
4916
+
if within(self.params.range, variable_range) {
4917
+
self.selected_value = Some(PatternMatchedValue::UseVariable {
4918
+
variable_name: name,
4919
+
variable_type: type_,
4920
+
use_location: use_.location,
4921
+
});
4922
+
// If we've found the variable to pattern match on, there's no
4923
+
// point in keeping traversing the AST.
4924
+
return;
4925
+
}
4926
+
}
4927
+
}
4928
+
4929
+
ast::visit::visit_typed_use(self, use_);
4880
4930
}
4881
4931
}
4882
4932
+50
compiler-core/src/language_server/tests/action.rs
+50
compiler-core/src/language_server/tests/action.rs
···
6988
6988
}
6989
6989
6990
6990
#[test]
6991
+
fn pattern_match_on_use_assignment() {
6992
+
assert_code_action!(
6993
+
PATTERN_MATCH_ON_VARIABLE,
6994
+
"
6995
+
pub fn main() {
6996
+
use var <- f
6997
+
}
6998
+
6999
+
fn f(g) { g(#(1, 2)) }
7000
+
",
7001
+
find_position_of("var").to_selection()
7002
+
);
7003
+
}
7004
+
7005
+
#[test]
7006
+
fn pattern_match_on_use_assignment_with_multiple_constructors() {
7007
+
assert_code_action!(
7008
+
PATTERN_MATCH_ON_VARIABLE,
7009
+
"
7010
+
pub type Wibble {
7011
+
Wobble
7012
+
Woo
7013
+
}
7014
+
7015
+
pub fn main() {
7016
+
use var <- f
7017
+
}
7018
+
7019
+
fn f(g) { g(Wobble) }
7020
+
",
7021
+
find_position_of("var").to_selection()
7022
+
);
7023
+
}
7024
+
7025
+
#[test]
7026
+
fn pattern_match_on_pattern_use_assignment() {
7027
+
assert_no_code_actions!(
7028
+
PATTERN_MATCH_ON_VARIABLE,
7029
+
"
7030
+
pub fn main() {
7031
+
use #(a, b) <- f
7032
+
}
7033
+
7034
+
fn f(g) { g(#(1, 2)) }
7035
+
",
7036
+
find_position_of("#").to_selection()
7037
+
);
7038
+
}
7039
+
7040
+
#[test]
6991
7041
fn pattern_match_on_argument_works_on_fn_arguments() {
6992
7042
assert_code_action!(
6993
7043
PATTERN_MATCH_ON_ARGUMENT,
+22
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__pattern_match_on_use_assignment.snap
+22
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__pattern_match_on_use_assignment.snap
···
1
+
---
2
+
source: compiler-core/src/language_server/tests/action.rs
3
+
expression: "\npub fn main() {\n use var <- f\n}\n\nfn f(g) { g(#(1, 2)) }\n"
4
+
---
5
+
----- BEFORE ACTION
6
+
7
+
pub fn main() {
8
+
use var <- f
9
+
↑
10
+
}
11
+
12
+
fn f(g) { g(#(1, 2)) }
13
+
14
+
15
+
----- AFTER ACTION
16
+
17
+
pub fn main() {
18
+
use var <- f
19
+
let #(value_0, value_1) = var
20
+
}
21
+
22
+
fn f(g) { g(#(1, 2)) }
+35
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__pattern_match_on_use_assignment_with_multiple_constructors.snap
+35
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__pattern_match_on_use_assignment_with_multiple_constructors.snap
···
1
+
---
2
+
source: compiler-core/src/language_server/tests/action.rs
3
+
expression: "\npub type Wibble {\n Wobble\n Woo\n}\n\npub fn main() {\n use var <- f\n}\n\nfn f(g) { g(Wobble) }\n"
4
+
---
5
+
----- BEFORE ACTION
6
+
7
+
pub type Wibble {
8
+
Wobble
9
+
Woo
10
+
}
11
+
12
+
pub fn main() {
13
+
use var <- f
14
+
↑
15
+
}
16
+
17
+
fn f(g) { g(Wobble) }
18
+
19
+
20
+
----- AFTER ACTION
21
+
22
+
pub type Wibble {
23
+
Wobble
24
+
Woo
25
+
}
26
+
27
+
pub fn main() {
28
+
use var <- f
29
+
case var {
30
+
Wobble -> todo
31
+
Woo -> todo
32
+
}
33
+
}
34
+
35
+
fn f(g) { g(Wobble) }