Skip to content

Commit

Permalink
feat(service): code action for removing mut
Browse files Browse the repository at this point in the history
  • Loading branch information
g-plane committed Jan 20, 2025
1 parent 368ed86 commit b822da0
Show file tree
Hide file tree
Showing 8 changed files with 472 additions and 0 deletions.
9 changes: 9 additions & 0 deletions crates/service/src/features/code_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ impl LanguageService {
}
}
}
SyntaxKind::GLOBAL_TYPE => {
if quickfix {
if let Some(action) =
remove_mut::act(self, uri, &line_index, &it, &params.context)
{
actions.push(CodeActionOrCommand::CodeAction(action));
}
}
}
_ => {}
}
node = it;
Expand Down
1 change: 1 addition & 0 deletions crates/service/src/refactorings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod fix_invalid_mem_arg;
pub mod func_header_join;
pub mod func_header_split;
pub mod if_br_to_br_if;
pub mod remove_mut;
79 changes: 79 additions & 0 deletions crates/service/src/refactorings/remove_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::{
helpers,
uri::{InternUri, UrisCtx},
LanguageService,
};
use line_index::LineIndex;
use lsp_types::{
CodeAction, CodeActionContext, CodeActionKind, NumberOrString, TextEdit, WorkspaceEdit,
};
use rowan::ast::support;
use std::collections::HashMap;
use wat_syntax::{SyntaxKind, SyntaxNode};

pub fn act(
service: &LanguageService,
uri: InternUri,
line_index: &LineIndex,
node: &SyntaxNode,
context: &CodeActionContext,
) -> Option<CodeAction> {
let mut_token =
support::token(node, SyntaxKind::KEYWORD).filter(|keyword| keyword.text() == "mut")?;
let token_lsp_range = helpers::rowan_range_to_lsp_range(line_index, mut_token.text_range());
let diagnostic = context
.diagnostics
.iter()
.find(|diagnostic| match &diagnostic.code {
Some(NumberOrString::String(code)) => {
code == "needless-mut" && diagnostic.range == token_lsp_range
}
_ => false,
})?;

let mut text_edits = Vec::with_capacity(4);
if let Some(l_paren) = support::token(node, SyntaxKind::L_PAREN) {
text_edits.push(TextEdit {
range: helpers::rowan_range_to_lsp_range(line_index, l_paren.text_range()),
new_text: "".into(),
});
}
text_edits.push(TextEdit {
range: token_lsp_range,
new_text: "".into(),
});
if let Some(whitespace) = mut_token
.next_token()
.filter(|token| token.kind() == SyntaxKind::WHITESPACE)
{
text_edits.push(TextEdit {
range: helpers::rowan_range_to_lsp_range(line_index, whitespace.text_range()),
new_text: "".into(),
});
}
if let Some(r_paren) = support::token(node, SyntaxKind::R_PAREN) {
text_edits.push(TextEdit {
range: helpers::rowan_range_to_lsp_range(line_index, r_paren.text_range()),
new_text: "".into(),
});
}

#[expect(clippy::mutable_key_type)]
if text_edits.is_empty() {
None
} else {
let mut changes = HashMap::with_capacity(1);
changes.insert(service.lookup_uri(uri), text_edits);
Some(CodeAction {
title: "Remove `mut`".into(),
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(WorkspaceEdit {
changes: Some(changes),
..Default::default()
}),
is_preferred: Some(true),
diagnostics: Some(vec![diagnostic.clone()]),
..Default::default()
})
}
}
1 change: 1 addition & 0 deletions crates/service/tests/code_action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod fix_invalid_mem_arg;
mod func_header_join;
mod func_header_split;
mod if_br_to_br_if;
mod remove_mut;

fn create_params(uri: Uri, range: Range) -> CodeActionParams {
CodeActionParams {
Expand Down
153 changes: 153 additions & 0 deletions crates/service/tests/code_action/remove_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use super::*;
use insta::assert_json_snapshot;
use lsp_types::{Diagnostic, NumberOrString, Position, Range, Uri};
use wat_service::LanguageService;

fn create_params(uri: Uri, range: Range, token_range: Range) -> CodeActionParams {
CodeActionParams {
text_document: TextDocumentIdentifier { uri },
range,
context: CodeActionContext {
diagnostics: vec![Diagnostic {
range: token_range,
code: Some(NumberOrString::String("needless-mut".into())),
message: "".into(),
..Default::default()
}],
only: None,
trigger_kind: None,
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}
}

#[test]
fn no_mut() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a i32))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(super::create_params(
uri,
Range::new(Position::new(2, 15), Position::new(2, 15)),
));
assert!(response.is_none());
}

#[test]
fn no_diagnostics() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a (mut i32)))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(super::create_params(
uri,
Range::new(Position::new(2, 15), Position::new(2, 15)),
));
assert!(response.is_none());
}

#[test]
fn unrelated_range() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a (mut i32))
(global $b (mut i32)))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(create_params(
uri,
Range::new(Position::new(2, 15), Position::new(2, 15)),
Range::new(Position::new(3, 14), Position::new(3, 17)),
));
assert!(response.is_none());
}

#[test]
fn unrelated_diagnostic() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a (mut i32))
(global $b (mut i32)))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(CodeActionParams {
text_document: TextDocumentIdentifier { uri },
range: Range::new(Position::new(2, 15), Position::new(2, 15)),
context: CodeActionContext {
diagnostics: vec![Diagnostic {
range: Range::new(Position::new(2, 15), Position::new(2, 15)),
code: Some(NumberOrString::String("global-mut".into())),
message: "".into(),
..Default::default()
}],
only: None,
trigger_kind: None,
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
});
assert!(response.is_none());
}

#[test]
fn simple() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a (mut i32)))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(create_params(
uri,
Range::new(Position::new(2, 15), Position::new(2, 15)),
Range::new(Position::new(2, 14), Position::new(2, 17)),
));
assert_json_snapshot!(response);
}

#[test]
fn missing_r_paren() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a (mut i32
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(create_params(
uri,
Range::new(Position::new(2, 15), Position::new(2, 15)),
Range::new(Position::new(2, 14), Position::new(2, 17)),
));
assert_json_snapshot!(response);
}

#[test]
fn with_comments() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global $a ((;a;) mut(;b;) i32)))
";
let mut service = LanguageService::default();
service.commit(uri.clone(), source.into());
let response = service.code_action(create_params(
uri,
Range::new(Position::new(2, 20), Position::new(2, 20)),
Range::new(Position::new(2, 20), Position::new(2, 23)),
));
assert_json_snapshot!(response);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
source: crates/service/tests/code_action/remove_mut.rs
expression: response
---
[
{
"title": "Remove `mut`",
"kind": "quickfix",
"diagnostics": [
{
"range": {
"start": {
"line": 2,
"character": 14
},
"end": {
"line": 2,
"character": 17
}
},
"code": "needless-mut",
"message": ""
}
],
"edit": {
"changes": {
"untitled:test": [
{
"range": {
"start": {
"line": 2,
"character": 13
},
"end": {
"line": 2,
"character": 14
}
},
"newText": ""
},
{
"range": {
"start": {
"line": 2,
"character": 14
},
"end": {
"line": 2,
"character": 17
}
},
"newText": ""
},
{
"range": {
"start": {
"line": 2,
"character": 17
},
"end": {
"line": 2,
"character": 18
}
},
"newText": ""
}
]
}
},
"isPreferred": true
}
]
Loading

0 comments on commit b822da0

Please sign in to comment.