Skip to content

Commit dc632c2

Browse files
GearsDatapackslpil
authored andcommitted
Allow selecting partial expressions
1 parent 7025900 commit dc632c2

16 files changed

+194
-31
lines changed

compiler-core/src/language_server/code_action.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8810,6 +8810,49 @@ impl<'a> ExtractFunction<'a> {
88108810

88118811
variables.push((name.clone(), type_.clone()));
88128812
}
8813+
8814+
fn can_extract(&self, location: SrcSpan) -> bool {
8815+
let expression_range = self.edits.src_span_to_lsp_range(location);
8816+
let selected_range = self.params.range;
8817+
8818+
// If the selected range doesn't touch the expression at all, then there
8819+
// is no reason to extract it.
8820+
if !overlaps(expression_range, selected_range) {
8821+
return false;
8822+
}
8823+
8824+
// Determine whether the selected range falls completely within the
8825+
// expression. For example:
8826+
// ```gleam
8827+
// pub fn main() {
8828+
// let something = {
8829+
// let a = 1
8830+
// let b = 2
8831+
// let c = a + b
8832+
// //^ The user has selected from here
8833+
// let d = a * b
8834+
// c / d
8835+
// // ^ Until here
8836+
// }
8837+
// }
8838+
// ```
8839+
//
8840+
// Here, the selected range does overlap with the `let something`
8841+
// statement; but we don't want to extract that whole statement! The
8842+
// user only wanted to extract the statements inside the block. So if
8843+
// the selected range falls completely within the expression, we ignore
8844+
// it and traverse the tree further until we find exactly what the user
8845+
// selected.
8846+
//
8847+
let selected_within_expression = selected_range.start > expression_range.start
8848+
&& selected_range.start < expression_range.end
8849+
&& selected_range.end > expression_range.start
8850+
&& selected_range.end < expression_range.end;
8851+
8852+
// If the selected range is completely within the expression, we don't
8853+
// want to extract it.
8854+
!selected_within_expression
8855+
}
88138856
}
88148857

88158858
impl<'ast> ast::visit::Visit<'ast> for ExtractFunction<'ast> {
@@ -8830,10 +8873,8 @@ impl<'ast> ast::visit::Visit<'ast> for ExtractFunction<'ast> {
88308873
// extracting just a single literal in any selection, which is of course
88318874
// not desired.
88328875
if self.function.is_none() {
8833-
let range = self.edits.src_span_to_lsp_range(expression.location());
8834-
88358876
// If this expression is fully selected, we mark it as being extracted.
8836-
if within(range, self.params.range) {
8877+
if self.can_extract(expression.location()) {
88378878
self.function = Some(ExtractedFunction::new(ExtractedValue::Expression(
88388879
expression,
88398880
)));
@@ -8843,8 +8884,7 @@ impl<'ast> ast::visit::Visit<'ast> for ExtractFunction<'ast> {
88438884
}
88448885

88458886
fn visit_typed_statement(&mut self, statement: &'ast TypedStatement) {
8846-
let range = self.edits.src_span_to_lsp_range(statement.location());
8847-
if within(range, self.params.range) {
8887+
if self.can_extract(statement.location()) {
88488888
match &mut self.function {
88498889
None => {
88508890
self.function = Some(ExtractedFunction::new(ExtractedValue::Statements(

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

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10245,7 +10245,7 @@ pub fn do_things(a, b) {
1024510245
",
1024610246
find_position_of("{")
1024710247
.nth_occurrence(2)
10248-
.select_until(find_position_of("}\n").under_char('\n'))
10248+
.select_until(find_position_of("}"))
1024910249
);
1025010250
}
1025110251

@@ -10261,7 +10261,7 @@ pub fn do_things(a, b) {
1026110261
result + 3
1026210262
}
1026310263
",
10264-
find_position_of("let").select_until(find_position_of("* b\n").under_char('\n'))
10264+
find_position_of("let").select_until(find_position_of("* b"))
1026510265
);
1026610266
}
1026710267

@@ -10277,7 +10277,7 @@ pub fn do_things(a, b) {
1027710277
result + 3
1027810278
}
1027910279
",
10280-
find_position_of("let").select_until(find_position_of("* new_b\n").under_char('\n'))
10280+
find_position_of("let").select_until(find_position_of("* new_b"))
1028110281
);
1028210282
}
1028310283

@@ -10296,7 +10296,7 @@ pub fn do_things(a, b) {
1029610296
result + 3
1029710297
}
1029810298
",
10299-
find_position_of("let").select_until(find_position_of("+ a * b\n").under_char('\n'))
10299+
find_position_of("let").select_until(find_position_of("+ a * b"))
1030010300
);
1030110301
}
1030210302

@@ -10311,7 +10311,7 @@ pub fn do_things(a, b) {
1031110311
wobble / wibble
1031210312
}
1031310313
",
10314-
find_position_of("let").select_until(find_position_of("* b\n").under_char('\n'))
10314+
find_position_of("let").select_until(find_position_of("* b"))
1031510315
);
1031610316
}
1031710317

@@ -10326,7 +10326,7 @@ pub fn do_things(a, b) {
1032610326
a
1032710327
}
1032810328
",
10329-
find_position_of("let").select_until(find_position_of("echo x\n").under_char('\n'))
10329+
find_position_of("let").select_until(find_position_of("echo x"))
1033010330
);
1033110331
}
1033210332

@@ -10345,7 +10345,7 @@ pub fn do_things(a, b) {
1034510345
result % 4
1034610346
}
1034710347
",
10348-
find_position_of("case").select_until(find_position_of("}\n").under_char('\n'))
10348+
find_position_of("case").select_until(find_position_of("}"))
1034910349
);
1035010350
}
1035110351

@@ -10369,7 +10369,7 @@ pub fn main() {
1036910369
}
1037010370
}
1037110371
",
10372-
find_position_of("case").select_until(find_position_of("}\n").under_char('\n'))
10372+
find_position_of("case").select_until(find_position_of("}"))
1037310373
);
1037410374
}
1037510375

@@ -10388,7 +10388,7 @@ pub fn main() {
1038810388
echo circumference
1038910389
}
1039010390
",
10391-
find_position_of("radius *.").select_until(find_position_of("2.0\n").under_char('\n'))
10391+
find_position_of("radius *.").select_until(find_position_of("2.0"))
1039210392
);
1039310393
}
1039410394

@@ -10412,7 +10412,7 @@ pub fn main() {
1041210412
echo string
1041310413
}
1041410414
"#,
10415-
find_position_of("case").select_until(find_position_of("}\n").under_char('\n'))
10415+
find_position_of("case").select_until(find_position_of("}"))
1041610416
);
1041710417
}
1041810418

@@ -10454,8 +10454,7 @@ pub fn do_things(a, b) {
1045410454
result + 3
1045510455
}
1045610456
"#,
10457-
find_position_of("= {")
10458-
.select_until(find_position_of("}\n").nth_occurrence(2).under_char('\n'))
10457+
find_position_of("= {").select_until(find_position_of("}").nth_occurrence(2))
1045910458
);
1046010459
}
1046110460

@@ -10478,7 +10477,63 @@ pub fn do_things(a, b) {
1047810477
result + 3
1047910478
}
1048010479
"#,
10481-
find_position_of("= {")
10482-
.select_until(find_position_of("}\n").nth_occurrence(5).under_char('\n'))
10480+
find_position_of("= {").select_until(find_position_of("}").nth_occurrence(5))
10481+
);
10482+
}
10483+
10484+
#[test]
10485+
fn extract_function_partially_selected() {
10486+
assert_code_action!(
10487+
EXTRACT_FUNCTION,
10488+
r#"
10489+
pub fn main() {
10490+
let a = 10
10491+
let b = 20
10492+
let c = a + b
10493+
10494+
echo c
10495+
}
10496+
"#,
10497+
find_position_of("a =").select_until(find_position_of("c ="))
10498+
);
10499+
}
10500+
10501+
#[test]
10502+
fn selected_statements_do_not_select_outer_block() {
10503+
// We want to make sure only the statements within the block are extracted,
10504+
// and not the block itself.
10505+
assert_code_action!(
10506+
EXTRACT_FUNCTION,
10507+
r#"
10508+
pub fn main() {
10509+
let c = {
10510+
let a = 10
10511+
let b = 20
10512+
a + b
10513+
}
10514+
10515+
echo c
10516+
}
10517+
"#,
10518+
find_position_of("let a").select_until(find_position_of("+ b"))
10519+
);
10520+
}
10521+
10522+
#[test]
10523+
fn no_code_action_to_extract_when_multiple_functions_are_selected() {
10524+
assert_no_code_actions!(
10525+
EXTRACT_FUNCTION,
10526+
r#"
10527+
pub fn main() {
10528+
let a = 10
10529+
a + 1
10530+
}
10531+
10532+
pub fn other() {
10533+
let b = 20
10534+
b * 2
10535+
}
10536+
"#,
10537+
find_position_of("let a").select_until(find_position_of("let b"))
1048310538
);
1048410539
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub fn do_things(a, b) {
1414
a * b
1515
▔▔▔▔▔▔▔▔▔
1616
}
17-
▔▔
17+
▔▔
1818
result + 3
1919
}
2020

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub fn do_things(a, b) {
1010
let b = 10 + b
1111
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1212
let result = a * b
13-
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
13+
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1414
result + 3
1515
}
1616

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "\npub fn main() {\n let a = 10\n let b = 20\n let c = a + b\n\n echo c\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
7+
pub fn main() {
8+
let a = 10
9+
▔▔▔▔▔▔
10+
let b = 20
11+
▔▔▔▔▔▔▔▔▔▔▔▔
12+
let c = a + b
13+
▔▔▔▔▔▔↑
14+
15+
echo c
16+
}
17+
18+
19+
----- AFTER ACTION
20+
21+
pub fn main() {
22+
let c = function()
23+
24+
echo c
25+
}
26+
27+
fn function() -> Int {
28+
let a = 10
29+
let b = 20
30+
let c = a + b
31+
c
32+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn do_things(a, b) {
1919
a * b
2020
▔▔▔▔▔▔▔▔▔
2121
}
22-
▔▔
22+
▔▔
2323
result + 3
2424
}
2525

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub fn do_things(a, b) {
1616
a * b
1717
▔▔▔▔▔▔▔▔▔
1818
}
19-
▔▔
19+
▔▔
2020
result + 3
2121
}
2222

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub fn do_things(a, b) {
1010
let new_b = 10 + b
1111
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1212
let result = new_a * new_b
13-
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
13+
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1414
result + 3
1515
}
1616

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub fn do_things(a, b) {
1616
}
1717
▔▔▔
1818
let result = first_part + a * b
19-
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
19+
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
2020
result + 3
2121
}
2222

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub fn main() {
1010
let radius = 4.5
1111

1212
let circumference = radius *. pi *. 2.0
13-
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
13+
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1414

1515
echo circumference
1616
}

0 commit comments

Comments
 (0)