Skip to content

Commit b37a216

Browse files
giacomocavalierilpil
authored andcommitted
Fix bug with extract variable top level statements
1 parent a02b6f0 commit b37a216

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

compiler-core/src/language_server/code_action.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -2605,12 +2605,18 @@ pub struct ExtractVariable<'a> {
26052605
module: &'a Module,
26062606
params: &'a CodeActionParams,
26072607
edits: TextEdits<'a>,
2608-
inside_capture_body: bool,
2608+
position: Option<ExtractVariablePosition>,
26092609
selected_expression: Option<SrcSpan>,
26102610
statement_before_selected_expression: Option<SrcSpan>,
26112611
latest_statement: Option<SrcSpan>,
26122612
}
26132613

2614+
#[derive(PartialEq, Eq, Copy, Clone)]
2615+
enum ExtractVariablePosition {
2616+
InsideCaptureBody,
2617+
TopLevelStatement,
2618+
}
2619+
26142620
impl<'a> ExtractVariable<'a> {
26152621
pub fn new(
26162622
module: &'a Module,
@@ -2621,7 +2627,7 @@ impl<'a> ExtractVariable<'a> {
26212627
module,
26222628
params,
26232629
edits: TextEdits::new(line_numbers),
2624-
inside_capture_body: false,
2630+
position: None,
26252631
selected_expression: None,
26262632
latest_statement: None,
26272633
statement_before_selected_expression: None,
@@ -2669,21 +2675,50 @@ impl<'ast> ast::visit::Visit<'ast> for ExtractVariable<'ast> {
26692675
// A capture body is comprised of just a single expression statement
26702676
// that is inserted by the compiler, we don't really want to put
26712677
// anything before that; so in this case we avoid tracking it.
2672-
if !self.inside_capture_body {
2678+
if self.position != Some(ExtractVariablePosition::InsideCaptureBody) {
26732679
self.latest_statement = Some(stmt.location());
26742680
}
2681+
2682+
let previous_position = self.position;
2683+
self.position = Some(ExtractVariablePosition::TopLevelStatement);
26752684
ast::visit::visit_typed_statement(self, stmt);
2685+
self.position = previous_position;
26762686
}
26772687

26782688
fn visit_typed_expr(&mut self, expr: &'ast TypedExpr) {
26792689
let expr_location = expr.location();
26802690
let expr_range = self.edits.src_span_to_lsp_range(expr_location);
2681-
if within(self.params.range, expr_range) {
2691+
2692+
// If the expression is a top level statement we don't want to extract
2693+
// it into a variable. It would mean we would turn this:
2694+
//
2695+
// ```gleam
2696+
// pub fn main() {
2697+
// let wibble = 1
2698+
// // ^ cursor here
2699+
// }
2700+
//
2701+
// // into:
2702+
//
2703+
// pub fn main() {
2704+
// let value = 1
2705+
// let wibble = value
2706+
// }
2707+
// ```
2708+
//
2709+
// Not all that useful!
2710+
//
2711+
if self.position != Some(ExtractVariablePosition::TopLevelStatement)
2712+
&& within(self.params.range, expr_range)
2713+
{
26822714
self.selected_expression = Some(expr_location);
26832715
self.statement_before_selected_expression = self.latest_statement;
26842716
}
26852717

2718+
let previous_position = self.position;
2719+
self.position = None;
26862720
ast::visit::visit_typed_expr(self, expr);
2721+
self.position = previous_position;
26872722
}
26882723

26892724
fn visit_typed_expr_fn(
@@ -2695,15 +2730,18 @@ impl<'ast> ast::visit::Visit<'ast> for ExtractVariable<'ast> {
26952730
body: &'ast [TypedStatement],
26962731
return_annotation: &'ast Option<ast::TypeAst>,
26972732
) {
2698-
self.inside_capture_body = match kind {
2733+
let previous_position = self.position;
2734+
self.position = match kind {
26992735
// If a fn is a capture `int.wibble(1, _)` its body will consist of
27002736
// just a single expression statement. When visiting we must record
27012737
// we're inside a capture body.
2702-
FunctionLiteralKind::Capture => true,
2703-
FunctionLiteralKind::Anonymous { .. } | FunctionLiteralKind::Use { .. } => false,
2738+
FunctionLiteralKind::Capture => Some(ExtractVariablePosition::InsideCaptureBody),
2739+
FunctionLiteralKind::Anonymous { .. } | FunctionLiteralKind::Use { .. } => {
2740+
self.position
2741+
}
27042742
};
27052743
ast::visit::visit_typed_expr_fn(self, location, type_, kind, args, body, return_annotation);
2706-
self.inside_capture_body = false;
2744+
self.position = previous_position;
27072745
}
27082746

27092747
// We don't want to offer the action if the cursor is over a variable

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

+24
Original file line numberDiff line numberDiff line change
@@ -3961,6 +3961,30 @@ fn extract_variable_in_block() {
39613961
);
39623962
}
39633963

3964+
#[test]
3965+
fn do_not_extract_top_level_expression_statement() {
3966+
assert_no_code_actions!(
3967+
EXTRACT_VARIABLE,
3968+
r#"pub fn main() {
3969+
1
3970+
}
3971+
"#,
3972+
find_position_of("1").to_selection()
3973+
);
3974+
}
3975+
3976+
#[test]
3977+
fn do_not_extract_top_level_expression_in_let_statement() {
3978+
assert_no_code_actions!(
3979+
EXTRACT_VARIABLE,
3980+
r#"pub fn main() {
3981+
let a = 1
3982+
}
3983+
"#,
3984+
find_position_of("1").to_selection()
3985+
);
3986+
}
3987+
39643988
#[test]
39653989
fn expand_function_capture() {
39663990
assert_code_action!(

0 commit comments

Comments
 (0)