From 795328cf2e91e9c8085e73f46fc24077605b5891 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:08:49 +0300 Subject: [PATCH 01/17] feat: add 'Annotate all top level type definitions' code action --- .../src/language_server/code_action.rs | 173 ++++++++++++++++++ compiler-core/src/language_server/engine.rs | 9 +- 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 26c819807cc..1afe42f4372 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1360,6 +1360,179 @@ impl<'a> AddAnnotations<'a> { } } +/// Code action to add type annotations to all top level definitions +/// +pub struct AnnotateTopLevelTypeDefinitions<'a> { + module: &'a Module, + params: &'a CodeActionParams, + edits: TextEdits<'a>, + printer: Printer<'a>, + is_hovering_definition: bool, +} + +impl<'a> AnnotateTopLevelTypeDefinitions<'a> { + pub fn new( + module: &'a Module, + line_numbers: &'a LineNumbers, + params: &'a CodeActionParams, + ) -> Self { + Self { + module, + params, + edits: TextEdits::new(line_numbers), + // We need to use the same printer for all the edits because otherwise + // we could get duplicate type variable names. + printer: Printer::new_without_type_variables(&module.ast.names), + is_hovering_definition: false, + } + } + + pub fn code_actions(mut self) -> Vec { + self.visit_typed_module(&self.module.ast); + + // We only want to trigger the action if we're over one of the definition in + // the module + if !self.is_hovering_definition { + return vec![]; + }; + + let mut action = Vec::with_capacity(1); + CodeActionBuilder::new("Annotate all top level type definitions") + .kind(CodeActionKind::REFACTOR_REWRITE) + .changes(self.params.text_document.uri.clone(), self.edits.edits) + .preferred(false) + .push_to(&mut action); + action + } +} + +impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelTypeDefinitions<'_> { + fn visit_typed_module_constant(&mut self, constant: &'ast TypedModuleConstant) { + // Since type variable names are local to definitions, any type variables + // in other parts of the module shouldn't affect what we print for the + // annotations of this constant. + self.printer.clear_type_variables(); + + let code_action_range = self.edits.src_span_to_lsp_range(constant.location); + + if overlaps(code_action_range, self.params.range) { + self.is_hovering_definition = true; + } + + // We don't need to add an annotation if there already is one + if constant.annotation.is_some() { + return; + } + + self.edits.insert( + constant.name_location.end, + format!(": {}", self.printer.print_type(&constant.type_)), + ); + } + + fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) { + // Since type variable names are local to definitions, any type variables + // in other parts of the module shouldn't affect what we print for the + // annotations of this functions. The only variables which cannot clash + // are ones defined in the signature of this function, which we register + // when we visit the parameters of this function inside `collect_type_variables`. + self.printer.clear_type_variables(); + collect_type_variables(&mut self.printer, fun); + + ast::visit::visit_typed_function(self, fun); + + let code_action_range = self.edits.src_span_to_lsp_range( + fun.body_start + .map(|body_start| SrcSpan { + start: fun.location.start, + end: body_start, + }) + .unwrap_or(fun.location), + ); + + if overlaps(code_action_range, self.params.range) { + self.is_hovering_definition = true; + } + + // Annotate each argument separately + for argument in fun.arguments.iter() { + // Don't annotate the argument if it's already annotated + if argument.annotation.is_some() { + continue; + } + + self.edits.insert( + argument.location.end, + format!(": {}", self.printer.print_type(&argument.type_)), + ); + } + + // Annotate the return type if it isn't already annotated + if fun.return_annotation.is_none() { + self.edits.insert( + fun.location.end, + format!(" -> {}", self.printer.print_type(&fun.return_type)), + ); + } + } + + fn visit_typed_expr_fn( + &mut self, + location: &'ast SrcSpan, + type_: &'ast Arc, + kind: &'ast FunctionLiteralKind, + arguments: &'ast [TypedArg], + body: &'ast Vec1, + return_annotation: &'ast Option, + ) { + ast::visit::visit_typed_expr_fn( + self, + location, + type_, + kind, + arguments, + body, + return_annotation, + ); + + // If the function doesn't have a head, we can't annotate it + let location = match kind { + // Function captures don't need any type annotations + FunctionLiteralKind::Capture { .. } => return, + FunctionLiteralKind::Anonymous { head } => head, + FunctionLiteralKind::Use { location } => location, + }; + + let code_action_range = self.edits.src_span_to_lsp_range(*location); + + if overlaps(code_action_range, self.params.range) { + self.is_hovering_definition = true; + } + + // Annotate each argument separately + for argument in arguments.iter() { + // Don't annotate the argument if it's already annotated + if argument.annotation.is_some() { + continue; + } + + self.edits.insert( + argument.location.end, + format!(": {}", self.printer.print_type(&argument.type_)), + ); + } + + // Annotate the return type if it isn't already annotated, and this is + // an anonymous function. + if return_annotation.is_none() && matches!(kind, FunctionLiteralKind::Anonymous { .. }) { + let return_type = &type_.return_type().expect("Type must be a function"); + let pretty_type = self.printer.print_type(return_type); + self.edits + .insert(location.end, format!(" -> {pretty_type}")); + } + } +} + struct TypeVariableCollector<'a, 'b> { printer: &'a mut Printer<'b>, } diff --git a/compiler-core/src/language_server/engine.rs b/compiler-core/src/language_server/engine.rs index ef30ce7b317..ef51ebcd70c 100644 --- a/compiler-core/src/language_server/engine.rs +++ b/compiler-core/src/language_server/engine.rs @@ -44,9 +44,9 @@ use std::{collections::HashSet, sync::Arc}; use super::{ DownloadDependencies, MakeLocker, code_action::{ - AddAnnotations, CodeActionBuilder, ConvertFromUse, ConvertToFunctionCall, ConvertToPipe, - ConvertToUse, ExpandFunctionCapture, ExtractConstant, ExtractVariable, - FillInMissingLabelledArgs, FillUnusedFields, FixBinaryOperation, + AddAnnotations, AnnotateTopLevelTypeDefinitions, CodeActionBuilder, ConvertFromUse, + ConvertToFunctionCall, ConvertToPipe, ConvertToUse, ExpandFunctionCapture, ExtractConstant, + ExtractVariable, FillInMissingLabelledArgs, FillUnusedFields, FixBinaryOperation, FixTruncatedBitArraySegment, GenerateDynamicDecoder, GenerateFunction, GenerateJsonEncoder, GenerateVariant, InlineVariable, InterpolateString, LetAssertToCase, PatternMatchOnValue, RedundantTupleInCaseSubject, RemoveEchos, RemoveUnusedImports, UseLabelShorthandSyntax, @@ -461,6 +461,9 @@ where ) .code_actions(); AddAnnotations::new(module, &lines, ¶ms).code_action(&mut actions); + actions.extend( + AnnotateTopLevelTypeDefinitions::new(module, &lines, ¶ms).code_actions(), + ); Ok(if actions.is_empty() { None } else { From 7ec8174d1fe8e08ac8a11bcc2a621dbbd6ff725d Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:39:50 +0300 Subject: [PATCH 02/17] chore: write tests + snapshots --- .../src/language_server/tests/action.rs | 56 +++++++++++++++++++ ..._action__add_multiple_annotations.snap.new | 29 ++++++++++ ...te_all_top_level_definitions_constant.snap | 29 ++++++++++ ...el_definitions_dont_affect_local_vars.snap | 31 ++++++++++ ...te_all_top_level_definitions_function.snap | 25 +++++++++ 5 files changed, 170 insertions(+) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_multiple_annotations.snap.new create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_constant.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_function.snap diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 7466c7a1af4..604138a6d52 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -112,6 +112,7 @@ const ASSIGN_UNUSED_RESULT: &str = "Assign unused Result value to `_`"; const ADD_MISSING_PATTERNS: &str = "Add missing patterns"; const ADD_ANNOTATION: &str = "Add type annotation"; const ADD_ANNOTATIONS: &str = "Add type annotations"; +const ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS: &str = "Annotate all top level type definitions"; const CONVERT_FROM_USE: &str = "Convert from `use`"; const CONVERT_TO_USE: &str = "Convert to `use`"; const EXTRACT_VARIABLE: &str = "Extract variable"; @@ -10686,3 +10687,58 @@ pub fn main() -> Nil { find_position_of("function").to_selection() ); } + +#[test] +fn annotate_all_top_level_definitions_dont_affect_local_vars() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + r#" +pub const answer = 42 + +pub fn add_two(thing) { + thing + 2 + +pub fn add_one(thing) { + let result = thing + 1 + result +} +"#, + find_position_of("fn").select_until(find_position_of("(")); +} + +#[test] +fn annotate_all_top_level_definitions_constant() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + r#" +pub const answer = 42 + +pub fn add_two(thing) { + thing + 2 +} + +pub fn add_one(thing) { + thing + 1 +} +"#, + find_position_of("const").select_until(find_position_of("=")) + ); +} + + +#[test] +fn annotate_all_top_level_definitions_function() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + r#" +pub fn add_two(thing) { + thing + 2 +} + +pub fn add_one(thing) { + thing + 1 +} + "#, + find_position_of("fn").select_until(find_position_of("(")) + ); +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_multiple_annotations.snap.new b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_multiple_annotations.snap.new new file mode 100644 index 00000000000..bada8ed8319 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_multiple_annotations.snap.new @@ -0,0 +1,29 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +assertion_line: 2928 +expression: "\npub const my_constant = 20\n\npub fn add_my_constant(value) {\n let result = value + my_constant\n result\n}\n" +snapshot_kind: text +--- +----- BEFORE ACTION + +pub const my_constant = 20 +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + +pub fn add_my_constant(value) { +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + let result = value + my_constant +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + result +▔▔▔▔▔▔▔▔ +} +↑ + + +----- AFTER ACTION + +pub const my_constant: Int = 20 + +pub fn add_my_constant(value: Int) -> Int { + let result = value + my_constant + result +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_constant.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_constant.snap new file mode 100644 index 00000000000..fcef5471142 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_constant.snap @@ -0,0 +1,29 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub const answer = 42\n\npub fn add_two(thing) {\n thing + 2\n}\n\npub fn add_one(thing) {\n thing + 1\n}\n" +--- +----- BEFORE ACTION + +pub const answer = 42 + ▔▔▔▔▔▔▔▔▔▔▔▔▔↑ + +pub fn add_two(thing) { + thing + 2 +} + +pub fn add_one(thing) { + thing + 1 +} + + +----- AFTER ACTION + +pub const answer: Int = 42 + +pub fn add_two(thing: Int) -> Int { + thing + 2 +} + +pub fn add_one(thing: Int) -> Int { + thing + 1 +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap new file mode 100644 index 00000000000..ae6760aa6eb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap @@ -0,0 +1,31 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub const answer = 42\n\npub fn add_two(thing) {\n thing + 2\n}\n\npub fn add_one(thing) {\n let result = thing + 1\n result\n}\n" +--- +----- BEFORE ACTION + +pub const answer = 42 + +pub fn add_two(thing) { + ▔▔▔▔▔▔▔▔▔▔↑ + thing + 2 +} + +pub fn add_one(thing) { + let result = thing + 1 + result +} + + +----- AFTER ACTION + +pub const answer: Int = 42 + +pub fn add_two(thing: Int) -> Int { + thing + 2 +} + +pub fn add_one(thing: Int) -> Int { + let result = thing + 1 + result +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_function.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_function.snap new file mode 100644 index 00000000000..578ec1631e4 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_function.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn add_two(thing) {\n thing + 2\n}\n\npub fn add_one(thing) {\n thing + 1\n}\n" +--- +----- BEFORE ACTION + +pub fn add_two(thing) { + ▔▔▔▔▔▔▔▔▔▔↑ + thing + 2 +} + +pub fn add_one(thing) { + thing + 1 +} + + +----- AFTER ACTION + +pub fn add_two(thing: Int) -> Int { + thing + 2 +} + +pub fn add_one(thing: Int) -> Int { + thing + 1 +} From 1193488799a25847ebf7db4c6210eacf01efc332 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:48:10 +0300 Subject: [PATCH 03/17] chore: CHANGELOG entry --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1be6a42ecdf..ce0e8746ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,43 @@ ## Unreleased +### Language server + +- The language server nof offers code action to add type annotations to all + functions and constants. For example, + + ```gleam + pub const answer = 42 + + pub fn add(x, y) { + x + y + } + + pub fn add_one(thing) { + // ^ Triggering "Annotate all top level type definitions" code action here + let result = add(thing, 1) + result + } + ``` + + Triggering the "Annotate all top level type definitions" code action over + the name of function `add_one` would result in following code: + + ```gleam + pub const answer: Int = 42 + + pub fn add(x: Int, y: Int) -> Int { + x + y + } + + pub fn add_one(thing: Int) -> Int { + let result = add(thing, 1) + result + } + ``` + + ([Andrey Kozhev](https://github.com/ankddev)) + ### Bug fixes - Fixed a bug where the "Extract function" code action would not properly From a11a58d9f3b4c465189f133457b7c360de35585c Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:09:32 +0300 Subject: [PATCH 04/17] chore: format code sample in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0e8746ab1..a83882a9d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ } pub fn add_one(thing) { - // ^ Triggering "Annotate all top level type definitions" code action here + // ^ Triggering "Annotate all top level type definitions" code action here let result = add(thing, 1) result } From 8daffd453502bb3feb441f773ff6fb885549f7bd Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:13:55 +0300 Subject: [PATCH 05/17] chore: remove test and snapshot --- .../src/language_server/tests/action.rs | 1 - ...el_definitions_dont_affect_local_vars.snap | 31 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 604138a6d52..2b64dc0f5be 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10725,7 +10725,6 @@ pub fn add_one(thing) { ); } - #[test] fn annotate_all_top_level_definitions_function() { assert_code_action!( diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap deleted file mode 100644 index ae6760aa6eb..00000000000 --- a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap +++ /dev/null @@ -1,31 +0,0 @@ ---- -source: compiler-core/src/language_server/tests/action.rs -expression: "\npub const answer = 42\n\npub fn add_two(thing) {\n thing + 2\n}\n\npub fn add_one(thing) {\n let result = thing + 1\n result\n}\n" ---- ------ BEFORE ACTION - -pub const answer = 42 - -pub fn add_two(thing) { - ▔▔▔▔▔▔▔▔▔▔↑ - thing + 2 -} - -pub fn add_one(thing) { - let result = thing + 1 - result -} - - ------ AFTER ACTION - -pub const answer: Int = 42 - -pub fn add_two(thing: Int) -> Int { - thing + 2 -} - -pub fn add_one(thing: Int) -> Int { - let result = thing + 1 - result -} From 8486ff05a1c2d81c3fdae9b641d7cadd9af8db7e Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:06:49 +0300 Subject: [PATCH 06/17] fix: typo in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a83882a9d73..29a022d652c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Language server -- The language server nof offers code action to add type annotations to all +- The language server now offers code action to add type annotations to all functions and constants. For example, ```gleam From c6e00f57f010024b943445eaf9cf9c7de324ec8a Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:12:04 +0300 Subject: [PATCH 07/17] refactor: don't visit expr_fn's in code action They're not top level definitions, I added them by mistake, so removing --- .../src/language_server/code_action.rs | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 1afe42f4372..cc55e0cd698 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1475,62 +1475,6 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelTypeDefinitions<'_> { ); } } - - fn visit_typed_expr_fn( - &mut self, - location: &'ast SrcSpan, - type_: &'ast Arc, - kind: &'ast FunctionLiteralKind, - arguments: &'ast [TypedArg], - body: &'ast Vec1, - return_annotation: &'ast Option, - ) { - ast::visit::visit_typed_expr_fn( - self, - location, - type_, - kind, - arguments, - body, - return_annotation, - ); - - // If the function doesn't have a head, we can't annotate it - let location = match kind { - // Function captures don't need any type annotations - FunctionLiteralKind::Capture { .. } => return, - FunctionLiteralKind::Anonymous { head } => head, - FunctionLiteralKind::Use { location } => location, - }; - - let code_action_range = self.edits.src_span_to_lsp_range(*location); - - if overlaps(code_action_range, self.params.range) { - self.is_hovering_definition = true; - } - - // Annotate each argument separately - for argument in arguments.iter() { - // Don't annotate the argument if it's already annotated - if argument.annotation.is_some() { - continue; - } - - self.edits.insert( - argument.location.end, - format!(": {}", self.printer.print_type(&argument.type_)), - ); - } - - // Annotate the return type if it isn't already annotated, and this is - // an anonymous function. - if return_annotation.is_none() && matches!(kind, FunctionLiteralKind::Anonymous { .. }) { - let return_type = &type_.return_type().expect("Type must be a function"); - let pretty_type = self.printer.print_type(return_type); - self.edits - .insert(location.end, format!(" -> {pretty_type}")); - } - } } struct TypeVariableCollector<'a, 'b> { From e6d548f60a26072cd11649241e4baa8230fb80c3 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:28:43 +0300 Subject: [PATCH 08/17] refactor: rename code action --- CHANGELOG.md | 4 ++-- compiler-core/src/language_server/code_action.rs | 8 ++++---- compiler-core/src/language_server/engine.rs | 7 +++---- compiler-core/src/language_server/tests/action.rs | 6 +++--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29a022d652c..fb5015c7fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,13 +15,13 @@ } pub fn add_one(thing) { - // ^ Triggering "Annotate all top level type definitions" code action here + // ^ Triggering "Annotate all top level definitions" code action here let result = add(thing, 1) result } ``` - Triggering the "Annotate all top level type definitions" code action over + Triggering the "Annotate all top level definitions" code action over the name of function `add_one` would result in following code: ```gleam diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index cc55e0cd698..28943683d05 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1362,7 +1362,7 @@ impl<'a> AddAnnotations<'a> { /// Code action to add type annotations to all top level definitions /// -pub struct AnnotateTopLevelTypeDefinitions<'a> { +pub struct AnnotateTopLevelDefinitions<'a> { module: &'a Module, params: &'a CodeActionParams, edits: TextEdits<'a>, @@ -1370,7 +1370,7 @@ pub struct AnnotateTopLevelTypeDefinitions<'a> { is_hovering_definition: bool, } -impl<'a> AnnotateTopLevelTypeDefinitions<'a> { +impl<'a> AnnotateTopLevelDefinitions<'a> { pub fn new( module: &'a Module, line_numbers: &'a LineNumbers, @@ -1397,7 +1397,7 @@ impl<'a> AnnotateTopLevelTypeDefinitions<'a> { }; let mut action = Vec::with_capacity(1); - CodeActionBuilder::new("Annotate all top level type definitions") + CodeActionBuilder::new("Annotate all top level definitions") .kind(CodeActionKind::REFACTOR_REWRITE) .changes(self.params.text_document.uri.clone(), self.edits.edits) .preferred(false) @@ -1406,7 +1406,7 @@ impl<'a> AnnotateTopLevelTypeDefinitions<'a> { } } -impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelTypeDefinitions<'_> { +impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { fn visit_typed_module_constant(&mut self, constant: &'ast TypedModuleConstant) { // Since type variable names are local to definitions, any type variables // in other parts of the module shouldn't affect what we print for the diff --git a/compiler-core/src/language_server/engine.rs b/compiler-core/src/language_server/engine.rs index ef51ebcd70c..50d8ea49664 100644 --- a/compiler-core/src/language_server/engine.rs +++ b/compiler-core/src/language_server/engine.rs @@ -44,7 +44,7 @@ use std::{collections::HashSet, sync::Arc}; use super::{ DownloadDependencies, MakeLocker, code_action::{ - AddAnnotations, AnnotateTopLevelTypeDefinitions, CodeActionBuilder, ConvertFromUse, + AddAnnotations, AnnotateTopLevelDefinitions, CodeActionBuilder, ConvertFromUse, ConvertToFunctionCall, ConvertToPipe, ConvertToUse, ExpandFunctionCapture, ExtractConstant, ExtractVariable, FillInMissingLabelledArgs, FillUnusedFields, FixBinaryOperation, FixTruncatedBitArraySegment, GenerateDynamicDecoder, GenerateFunction, GenerateJsonEncoder, @@ -461,9 +461,8 @@ where ) .code_actions(); AddAnnotations::new(module, &lines, ¶ms).code_action(&mut actions); - actions.extend( - AnnotateTopLevelTypeDefinitions::new(module, &lines, ¶ms).code_actions(), - ); + actions + .extend(AnnotateTopLevelDefinitions::new(module, &lines, ¶ms).code_actions()); Ok(if actions.is_empty() { None } else { diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 2b64dc0f5be..a31dfad188f 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -112,7 +112,7 @@ const ASSIGN_UNUSED_RESULT: &str = "Assign unused Result value to `_`"; const ADD_MISSING_PATTERNS: &str = "Add missing patterns"; const ADD_ANNOTATION: &str = "Add type annotation"; const ADD_ANNOTATIONS: &str = "Add type annotations"; -const ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS: &str = "Annotate all top level type definitions"; +const ANNOTATE_TOP_LEVEL_DEFINITIONS: &str = "Annotate all top level definitions"; const CONVERT_FROM_USE: &str = "Convert from `use`"; const CONVERT_TO_USE: &str = "Convert to `use`"; const EXTRACT_VARIABLE: &str = "Extract variable"; @@ -10691,7 +10691,7 @@ pub fn main() -> Nil { #[test] fn annotate_all_top_level_definitions_dont_affect_local_vars() { assert_code_action!( - ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + ANNOTATE_TOP_LEVEL_DEFINITIONS, r#" pub const answer = 42 @@ -10709,7 +10709,7 @@ pub fn add_one(thing) { #[test] fn annotate_all_top_level_definitions_constant() { assert_code_action!( - ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + ANNOTATE_TOP_LEVEL_DEFINITIONS, r#" pub const answer = 42 From 78f447f27f79dcbb9db0a46b93945251f7f28e2d Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:24:37 +0300 Subject: [PATCH 09/17] test: write more tests for code action --- .../src/language_server/tests/action.rs | 65 +++++++++++++++++++ ...level_definitions_partially_annotated.snap | 31 +++++++++ 2 files changed, 96 insertions(+) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_partially_annotated.snap diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index a31dfad188f..0541f5e5c6c 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10741,3 +10741,68 @@ pub fn add_one(thing) { find_position_of("fn").select_until(find_position_of("(")) ); } + +fn annotate_all_top_level_definitions_already_annotated() { + assert_no_code_actions!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +pub const answer: Int = 42 + +pub fn add_two(thing: Int) -> { + thing + 2 +} + +pub fn add_one(thing: Int) -> Int { + thing + 1 +} +"#, + find_position_of("fn").select_until(find_position_of("(")) + ); +} + +#[test] +fn annotate_all_top_level_definitions_inside_body() { + assert_no_code_actions!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +pub fn add_one(thing) { + thing + 1 +} +"#, + find_position_of("fn").select_until(find_position_of("(")) + ); +} + +#[test] +fn annotate_all_top_level_definitions_partially_annotated() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +pub const answer: Int = 42 +pub const another_answer = 43 + +pub fn add_two(thing) -> Int { + thing + 2 +} + +pub fn add_one(thing: Int) { + thing + 1 +} +"#, + find_position_of("fn").select_until(find_position_of("(")) + ); +} + +// FIXME: make test working +// #[test] +// fn annotate_all_top_level_definitions_with_two_generic_functions() { +// assert_code_action!( +// ANNOTATE_TOP_LEVEL_DEFINITIONS, +// r#" +// fn wibble(one) { todo } + +// fn wobble(other) { todo } +// "#, +// find_position_of("wobble").to_selection() +// ); +// } diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_partially_annotated.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_partially_annotated.snap new file mode 100644 index 00000000000..e1453798b46 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_partially_annotated.snap @@ -0,0 +1,31 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub const answer: Int = 42\npub const another_answer = 43\n\npub fn add_two(thing) -> Int {\n thing + 2\n}\n\npub fn add_one(thing: Int) {\n thing + 1\n}\n" +--- +----- BEFORE ACTION + +pub const answer: Int = 42 +pub const another_answer = 43 + +pub fn add_two(thing) -> Int { + ▔▔▔▔▔▔▔▔▔▔↑ + thing + 2 +} + +pub fn add_one(thing: Int) { + thing + 1 +} + + +----- AFTER ACTION + +pub const answer: Int = 42 +pub const another_answer: Int = 43 + +pub fn add_two(thing: Int) -> Int { + thing + 2 +} + +pub fn add_one(thing: Int) -> Int { + thing + 1 +} From d622179881f1830478d6b0eb54106b4e7218fd03 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:28:10 +0300 Subject: [PATCH 10/17] fix(test): forgot to change selection text --- compiler-core/src/language_server/tests/action.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 0541f5e5c6c..a854d7eec5d 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10769,7 +10769,7 @@ pub fn add_one(thing) { thing + 1 } "#, - find_position_of("fn").select_until(find_position_of("(")) + find_position_of("thing + 1").to_selection() ); } From 785aff9e0be972cd9bc696b4f3f2d1e893e2ce6f Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 07:00:00 +0300 Subject: [PATCH 11/17] fix(LS): make visiters ignore type variables from other definitions --- .../src/language_server/code_action.rs | 30 +++++--------- .../src/language_server/tests/action.rs | 40 +++++++++++++------ ...s_with_constant_and_generic_functions.snap | 21 ++++++++++ ...efinitions_with_two_generic_functions.snap | 17 ++++++++ 4 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_constant_and_generic_functions.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_two_generic_functions.snap diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 28943683d05..a9dae181ee9 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1366,7 +1366,6 @@ pub struct AnnotateTopLevelDefinitions<'a> { module: &'a Module, params: &'a CodeActionParams, edits: TextEdits<'a>, - printer: Printer<'a>, is_hovering_definition: bool, } @@ -1380,9 +1379,6 @@ impl<'a> AnnotateTopLevelDefinitions<'a> { module, params, edits: TextEdits::new(line_numbers), - // We need to use the same printer for all the edits because otherwise - // we could get duplicate type variable names. - printer: Printer::new_without_type_variables(&module.ast.names), is_hovering_definition: false, } } @@ -1408,11 +1404,6 @@ impl<'a> AnnotateTopLevelDefinitions<'a> { impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { fn visit_typed_module_constant(&mut self, constant: &'ast TypedModuleConstant) { - // Since type variable names are local to definitions, any type variables - // in other parts of the module shouldn't affect what we print for the - // annotations of this constant. - self.printer.clear_type_variables(); - let code_action_range = self.edits.src_span_to_lsp_range(constant.location); if overlaps(code_action_range, self.params.range) { @@ -1426,18 +1417,19 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { self.edits.insert( constant.name_location.end, - format!(": {}", self.printer.print_type(&constant.type_)), + format!( + ": {}", + // Create new printer to ignore type variables from other definitions + Printer::new_without_type_variables(&self.module.ast.names) + .print_type(&constant.type_) + ), ); } fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) { - // Since type variable names are local to definitions, any type variables - // in other parts of the module shouldn't affect what we print for the - // annotations of this functions. The only variables which cannot clash - // are ones defined in the signature of this function, which we register - // when we visit the parameters of this function inside `collect_type_variables`. - self.printer.clear_type_variables(); - collect_type_variables(&mut self.printer, fun); + // Create new printer to ignore type variables from other definitions + let mut printer = Printer::new_without_type_variables(&self.module.ast.names); + collect_type_variables(&mut printer, fun); ast::visit::visit_typed_function(self, fun); @@ -1463,7 +1455,7 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { self.edits.insert( argument.location.end, - format!(": {}", self.printer.print_type(&argument.type_)), + format!(": {}", printer.print_type(&argument.type_)), ); } @@ -1471,7 +1463,7 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { if fun.return_annotation.is_none() { self.edits.insert( fun.location.end, - format!(" -> {}", self.printer.print_type(&fun.return_type)), + format!(" -> {}", printer.print_type(&fun.return_type)), ); } } diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index a854d7eec5d..8e4e4b26176 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10793,16 +10793,30 @@ pub fn add_one(thing: Int) { ); } -// FIXME: make test working -// #[test] -// fn annotate_all_top_level_definitions_with_two_generic_functions() { -// assert_code_action!( -// ANNOTATE_TOP_LEVEL_DEFINITIONS, -// r#" -// fn wibble(one) { todo } - -// fn wobble(other) { todo } -// "#, -// find_position_of("wobble").to_selection() -// ); -// } +#[test] +fn annotate_all_top_level_definitions_with_two_generic_functions() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +fn wibble(one) { todo } + +fn wobble(other) { todo } +"#, + find_position_of("wobble").to_selection() + ); +} + +#[test] +fn annotate_all_top_level_definitions_with_constant_and_generic_functions() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +const answer = 42 + +fn wibble(one) { todo } + +fn wobble(other) { todo } +"#, + find_position_of("wobble").to_selection() + ); +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_constant_and_generic_functions.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_constant_and_generic_functions.snap new file mode 100644 index 00000000000..e25a5c6d4e0 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_constant_and_generic_functions.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\nconst answer = 42\n\nfn wibble(one) { todo }\n\nfn wobble(other) { todo }\n" +--- +----- BEFORE ACTION + +const answer = 42 + +fn wibble(one) { todo } + +fn wobble(other) { todo } + ↑ + + +----- AFTER ACTION + +const answer: Int = 42 + +fn wibble(one: a) -> b { todo } + +fn wobble(other: a) -> b { todo } diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_two_generic_functions.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_two_generic_functions.snap new file mode 100644 index 00000000000..4a5dc43887a --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_two_generic_functions.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\nfn wibble(one) { todo }\n\nfn wobble(other) { todo }\n" +--- +----- BEFORE ACTION + +fn wibble(one) { todo } + +fn wobble(other) { todo } + ↑ + + +----- AFTER ACTION + +fn wibble(one: a) -> b { todo } + +fn wobble(other: a) -> b { todo } From ef530832ef4bfc11c3e170487cd3823e110ef232 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:08:24 +0300 Subject: [PATCH 12/17] refactor: don't visit entire AST of function Co-authored-by: Giacomo Cavalieri --- compiler-core/src/language_server/code_action.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index a9dae181ee9..e3f1d044b40 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1431,7 +1431,6 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { let mut printer = Printer::new_without_type_variables(&self.module.ast.names); collect_type_variables(&mut printer, fun); - ast::visit::visit_typed_function(self, fun); let code_action_range = self.edits.src_span_to_lsp_range( fun.body_start From eed04b385b96684bd2cb7996a8690d4276bb3cdf Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:24:27 +0300 Subject: [PATCH 13/17] fix(test): syntax error --- compiler-core/src/language_server/tests/action.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 8e4e4b26176..71250905d49 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10748,7 +10748,7 @@ fn annotate_all_top_level_definitions_already_annotated() { r#" pub const answer: Int = 42 -pub fn add_two(thing: Int) -> { +pub fn add_two(thing: Int) -> Int { thing + 2 } From 0e1691bc548cc67122dcd3648a93e82dea422deb Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:25:40 +0300 Subject: [PATCH 14/17] test: add new test with partially annotated generic function --- .../src/language_server/tests/action.rs | 13 +++++++++++++ ...th_partially_annotated_generic_function.snap | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_partially_annotated_generic_function.snap diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 71250905d49..52ed06c64b2 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10793,6 +10793,19 @@ pub fn add_one(thing: Int) { ); } +#[test] +fn annotate_all_top_level_definitions_with_partially_annotated_generic_function() { + assert_code_action!( + ANNOTATE_TOP_LEVEL_DEFINITIONS, + r#" +pub fn wibble(a: a, b, c: c, d) { + todo +} +"#, + find_position_of("wibble").to_selection() + ); +} + #[test] fn annotate_all_top_level_definitions_with_two_generic_functions() { assert_code_action!( diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_partially_annotated_generic_function.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_partially_annotated_generic_function.snap new file mode 100644 index 00000000000..e2ab9e419cc --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__annotate_all_top_level_definitions_with_partially_annotated_generic_function.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn wibble(a: a, b, c: c, d) {\n todo\n}\n" +--- +----- BEFORE ACTION + +pub fn wibble(a: a, b, c: c, d) { + ↑ + todo +} + + +----- AFTER ACTION + +pub fn wibble(a: a, b: b, c: c, d: d) -> e { + todo +} From 9110dd5985bc76f492734cdc4e0507aa5c87d393 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:31:16 +0300 Subject: [PATCH 15/17] style: reformat code --- compiler-core/src/language_server/code_action.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index e3f1d044b40..32ad19279d2 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1431,7 +1431,6 @@ impl<'ast> ast::visit::Visit<'ast> for AnnotateTopLevelDefinitions<'_> { let mut printer = Printer::new_without_type_variables(&self.module.ast.names); collect_type_variables(&mut printer, fun); - let code_action_range = self.edits.src_span_to_lsp_range( fun.body_start .map(|body_start| SrcSpan { From 50c64404f571d901cb013abbeb45ed59edd9bbbe Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Wed, 1 Oct 2025 16:24:45 +0300 Subject: [PATCH 16/17] fix: code action showed up for annotated functions --- compiler-core/src/language_server/code_action.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 32ad19279d2..f34d295fa30 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1388,7 +1388,7 @@ impl<'a> AnnotateTopLevelDefinitions<'a> { // We only want to trigger the action if we're over one of the definition in // the module - if !self.is_hovering_definition { + if !self.is_hovering_definition || self.edits.edits.is_empty() { return vec![]; }; From b0110945515093f9976e2d1a1fde10bf67dff472 Mon Sep 17 00:00:00 2001 From: Andrey <190183925+ankddev@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:15:47 +0300 Subject: [PATCH 17/17] fix(test): some errors while resolving conflicts --- .../src/language_server/tests/action.rs | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 52ed06c64b2..0b53e7c593a 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -10688,24 +10688,6 @@ pub fn main() -> Nil { ); } -#[test] -fn annotate_all_top_level_definitions_dont_affect_local_vars() { - assert_code_action!( - ANNOTATE_TOP_LEVEL_DEFINITIONS, - r#" -pub const answer = 42 - -pub fn add_two(thing) { - thing + 2 - -pub fn add_one(thing) { - let result = thing + 1 - result -} -"#, - find_position_of("fn").select_until(find_position_of("(")); -} - #[test] fn annotate_all_top_level_definitions_constant() { assert_code_action!( @@ -10728,20 +10710,21 @@ pub fn add_one(thing) { #[test] fn annotate_all_top_level_definitions_function() { assert_code_action!( - ANNOTATE_TOP_LEVEL_TYPE_DEFINITIONS, + ANNOTATE_TOP_LEVEL_DEFINITIONS, r#" pub fn add_two(thing) { - thing + 2 + thing + 2 } pub fn add_one(thing) { - thing + 1 + thing + 1 } - "#, +"#, find_position_of("fn").select_until(find_position_of("(")) ); } +#[test] fn annotate_all_top_level_definitions_already_annotated() { assert_no_code_actions!( ANNOTATE_TOP_LEVEL_DEFINITIONS,