Skip to content

Commit d1c7cca

Browse files
giacomocavalierilpil
authored andcommitted
add code action to remove opaque from private types
1 parent 0d6b4d3 commit d1c7cca

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@
7575

7676
### Language server
7777

78+
- The language server now offers a quick fix to remove `opaque` from a private
79+
type:
80+
81+
```gleam
82+
opaque type Wibble {
83+
// ^^^ This is an error!
84+
Wobble
85+
}
86+
```
87+
88+
If you hover over the type and trigger the quick fix, the language server will
89+
automatically remove the `opaque` keyword:
90+
91+
```gleam
92+
type Wibble {
93+
Wobble
94+
}
95+
```
96+
97+
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
98+
7899
### Formatter
79100

80101
### Bug fixes

compiler-core/src/language_server/code_action.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7490,3 +7490,75 @@ impl<'ast> ast::visit::Visit<'ast> for RemoveBlock<'ast> {
74907490
ast::visit::visit_typed_expr_block(self, location, statements);
74917491
}
74927492
}
7493+
7494+
/// Code action to remove `opaque` from a private type.
7495+
///
7496+
pub struct RemovePrivateOpaque<'a> {
7497+
module: &'a Module,
7498+
params: &'a CodeActionParams,
7499+
edits: TextEdits<'a>,
7500+
opaque_span: Option<SrcSpan>,
7501+
}
7502+
7503+
impl<'a> RemovePrivateOpaque<'a> {
7504+
pub fn new(
7505+
module: &'a Module,
7506+
line_numbers: &'a LineNumbers,
7507+
params: &'a CodeActionParams,
7508+
) -> Self {
7509+
Self {
7510+
module,
7511+
params,
7512+
edits: TextEdits::new(line_numbers),
7513+
opaque_span: None,
7514+
}
7515+
}
7516+
7517+
pub fn code_actions(mut self) -> Vec<CodeAction> {
7518+
self.visit_typed_module(&self.module.ast);
7519+
7520+
let Some(opaque_span) = self.opaque_span else {
7521+
return vec![];
7522+
};
7523+
7524+
self.edits.delete(opaque_span);
7525+
7526+
let mut action = Vec::with_capacity(1);
7527+
CodeActionBuilder::new("Remove opaque from private type")
7528+
.kind(CodeActionKind::QUICKFIX)
7529+
.changes(self.params.text_document.uri.clone(), self.edits.edits)
7530+
.preferred(true)
7531+
.push_to(&mut action);
7532+
action
7533+
}
7534+
}
7535+
7536+
impl<'ast> ast::visit::Visit<'ast> for RemovePrivateOpaque<'ast> {
7537+
fn visit_typed_definition(&mut self, def: &'ast ast::TypedDefinition) {
7538+
// This code action is only relevant for type definitions, so we don't
7539+
// waste any time visiting definitions that are not relevant.
7540+
match def {
7541+
ast::Definition::Function(_)
7542+
| ast::Definition::TypeAlias(_)
7543+
| ast::Definition::Import(_)
7544+
| ast::Definition::ModuleConstant(_) => (),
7545+
ast::Definition::CustomType(custom_type) => {
7546+
self.visit_typed_custom_type(custom_type);
7547+
}
7548+
}
7549+
}
7550+
7551+
fn visit_typed_custom_type(&mut self, custom_type: &'ast ast::TypedCustomType) {
7552+
let custom_type_range = self.edits.src_span_to_lsp_range(custom_type.location);
7553+
if !within(self.params.range, custom_type_range) {
7554+
return;
7555+
}
7556+
7557+
if custom_type.opaque && custom_type.publicity.is_private() {
7558+
self.opaque_span = Some(SrcSpan {
7559+
start: custom_type.location.start,
7560+
end: custom_type.location.start + 7,
7561+
})
7562+
}
7563+
}
7564+
}

compiler-core/src/language_server/engine.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use crate::{
1212
config::PackageConfig,
1313
io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter},
1414
language_server::{
15-
code_action::RemoveBlock, compiler::LspProjectCompiler, files::FileSystemProxy,
15+
code_action::{RemoveBlock, RemovePrivateOpaque},
16+
compiler::LspProjectCompiler,
17+
files::FileSystemProxy,
1618
progress::ProgressReporter,
1719
},
1820
line_numbers::LineNumbers,
@@ -437,6 +439,7 @@ where
437439
actions.extend(InlineVariable::new(module, &lines, &params).code_actions());
438440
actions.extend(WrapInBlock::new(module, &lines, &params).code_actions());
439441
actions.extend(RemoveBlock::new(module, &lines, &params).code_actions());
442+
actions.extend(RemovePrivateOpaque::new(module, &lines, &params).code_actions());
440443
GenerateDynamicDecoder::new(module, &lines, &params, &mut actions).code_actions();
441444
GenerateJsonEncoder::new(
442445
module,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ const REMOVE_ALL_ECHOS_FROM_THIS_MODULE: &str = "Remove all `echo`s from this mo
131131
const WRAP_IN_BLOCK: &str = "Wrap in block";
132132
const GENERATE_VARIANT: &str = "Generate variant";
133133
const REMOVE_BLOCK: &str = "Remove block";
134+
const REMOVE_OPAQUE_FROM_PRIVATE_TYPE: &str = "Remove opaque from private type";
134135

135136
macro_rules! assert_code_action {
136137
($title:expr, $code:literal, $range:expr $(,)?) => {
@@ -9132,3 +9133,15 @@ fn remove_block_does_not_unwrap_a_block_with_multiple_statements() {
91329133
find_position_of("1").to_selection()
91339134
);
91349135
}
9136+
9137+
#[test]
9138+
fn remove_opaque_from_private_type() {
9139+
assert_code_action!(
9140+
REMOVE_OPAQUE_FROM_PRIVATE_TYPE,
9141+
"opaque type Wibble {
9142+
Wobble
9143+
}
9144+
",
9145+
find_position_of("Wibble").to_selection()
9146+
);
9147+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
source: compiler-core/src/language_server/tests/action.rs
3+
expression: "opaque type Wibble {\n Wobble\n}\n"
4+
---
5+
----- BEFORE ACTION
6+
opaque type Wibble {
7+
8+
Wobble
9+
}
10+
11+
12+
----- AFTER ACTION
13+
type Wibble {
14+
Wobble
15+
}

0 commit comments

Comments
 (0)