diff --git a/CHANGELOG.md b/CHANGELOG.md index 740bae10b..9d63be995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -318,6 +318,10 @@ ([Gavin Morrow](https://github.com/gavinmorrow)) +- The language server now offers a code action to fix the new deprecated pipeline + syntax. + ([Surya Rose](https://github.com/GearsDatapacks)) + ### Formatter - Performance of the formatter has been improved. diff --git a/docs/v2.md b/docs/v2.md index 07b11ddcb..2388ac9ac 100644 --- a/docs/v2.md +++ b/docs/v2.md @@ -59,4 +59,4 @@ In Gleam v2, we want to remove the second option, so `a |> b(c)` always desugars to `b(a, c)`, regardless of the types. - [x] Emits warning when used. -- [ ] Code action to rewrite to desired syntax +- [x] Code action to rewrite to desired syntax diff --git a/language-server/src/code_action.rs b/language-server/src/code_action.rs index fe224a7c4..a32c7b002 100644 --- a/language-server/src/code_action.rs +++ b/language-server/src/code_action.rs @@ -12913,3 +12913,52 @@ impl<'a> DiscardUnusedVariable<'a> { action } } + +pub fn code_action_fix_deprecated_pipe( + module: &Module, + line_numbers: &LineNumbers, + params: &CodeActionParams, + actions: &mut Vec, +) { + let uri = ¶ms.text_document.uri; + let mut deprecated_pipes: Vec<&SrcSpan> = module + .ast + .type_info + .warnings + .iter() + .filter_map(|warning| { + if let type_::Warning::PipeIntoCallWhichReturnsFunction { location } = warning { + Some(location) + } else { + None + } + }) + .collect(); + + if deprecated_pipes.is_empty() { + return; + } + + // Sort spans by start position, with longer spans coming first + deprecated_pipes.sort_by_key(|span| (span.start, -(span.len() as i64))); + + for location in deprecated_pipes { + let range = src_span_to_lsp_range(*location, line_numbers); + + // Check if the cursor is within this span + if !within(params.range, range) { + continue; + } + + let edit = TextEdit { + range: Range::new(range.end, range.end), + new_text: "()".into(), + }; + + CodeActionBuilder::new("Add extra parentheses") + .kind(CodeActionKind::QuickFix) + .changes(uri.clone(), vec![edit]) + .preferred(true) + .push_to(actions); + } +} diff --git a/language-server/src/engine.rs b/language-server/src/engine.rs index d3ea26593..0ba798b97 100644 --- a/language-server/src/engine.rs +++ b/language-server/src/engine.rs @@ -41,7 +41,7 @@ use std::{ use crate::{ code_action::{ DiscardUnusedVariable, RemoveRedundantRecordUpdate, ReplaceUnderscoreWithType, - type_errors_for_module, + code_action_fix_deprecated_pipe, type_errors_for_module, }, reference::find_module_references_in_module, rename::{rename_module_alias, rename_module_occurrences, rename_type_variable}, @@ -554,6 +554,7 @@ where .code_actions(), ); actions.extend(DiscardUnusedVariable::new(module, &lines, ¶ms).code_actions()); + code_action_fix_deprecated_pipe(module, &lines, ¶ms, &mut actions); actions.sort_by_key(|one| { let preferred_key = if one.is_preferred == Some(true) { 0 } else { 1 }; diff --git a/language-server/src/tests/action.rs b/language-server/src/tests/action.rs index dd8273ea0..50ecba88e 100644 --- a/language-server/src/tests/action.rs +++ b/language-server/src/tests/action.rs @@ -207,6 +207,7 @@ const WRAP_IN_ANONYMOUS_FUNCTION: &str = "Wrap in anonymous function"; const UNWRAP_ANONYMOUS_FUNCTION: &str = "Remove anonymous function wrapper"; const REMOVE_REDUNDANT_RECORD_UPDATE: &str = "Remove redundant record update"; const DISCARD_UNUSED_VARIABLE: &str = "Discard unused variable"; +const ADD_EXTRA_PARENTHESES: &str = "Add extra parentheses"; macro_rules! assert_code_action { ($title:expr, $code:literal, $range_selector:expr $(,)?) => { @@ -15400,3 +15401,22 @@ fn convert_to_function_call_on_last_step_single_line() { find_position_of("wobble").to_selection() ); } + +#[test] +fn fix_deprecated_pipe_syntax() { + assert_code_action!( + ADD_EXTRA_PARENTHESES, + " +pub fn main() { + 1 |> wibble(2) +} + +pub fn wibble(a) { + fn(b) { + #(a, b) + } +} +", + find_position_of("wibble").under_char('i').to_selection() + ); +} diff --git a/language-server/src/tests/snapshots/gleam_language_server__tests__action__fix_deprecated_pipe_syntax.snap b/language-server/src/tests/snapshots/gleam_language_server__tests__action__fix_deprecated_pipe_syntax.snap new file mode 100644 index 000000000..6b9bf3a31 --- /dev/null +++ b/language-server/src/tests/snapshots/gleam_language_server__tests__action__fix_deprecated_pipe_syntax.snap @@ -0,0 +1,29 @@ +--- +source: language-server/src/tests/action.rs +expression: "\npub fn main() {\n 1 |> wibble(2)\n}\n\npub fn wibble(a) {\n fn(b) {\n #(a, b)\n }\n}\n" +--- +----- BEFORE ACTION + +pub fn main() { + 1 |> wibble(2) + ↑ +} + +pub fn wibble(a) { + fn(b) { + #(a, b) + } +} + + +----- AFTER ACTION + +pub fn main() { + 1 |> wibble(2)() +} + +pub fn wibble(a) { + fn(b) { + #(a, b) + } +}