Relocate and refactor quickfix code into its own module

We're going to have more quickfixes, to it's best not to overload the
  'server' module. Plus, there's a lot of boilerplate around the
  quickfixes so we might want to factor it out.
This commit is contained in:
KtorZ 2023-10-21 12:00:58 +02:00
parent 763516eb96
commit e48ac6b592
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 92 additions and 80 deletions

View File

@ -8,6 +8,7 @@ mod cast;
mod edits; mod edits;
pub mod error; pub mod error;
mod line_numbers; mod line_numbers;
mod quickfix;
pub mod server; pub mod server;
mod utils; mod utils;

View File

@ -0,0 +1,66 @@
use crate::{edits, server::lsp_project::LspProject};
use std::collections::HashMap;
const UNKNOWN_VARIABLE: &str = "aiken::check::unknown::variable";
const UNKNOWN_MODULE: &str = "aiken::check::unknown::module";
/// Errors for which we can provide quickfixes
pub enum Quickfix {
UnknownVariable,
}
fn match_code(diagnostic: &lsp_types::Diagnostic, expected: &str) -> bool {
diagnostic.code == Some(lsp_types::NumberOrString::String(expected.to_string()))
}
pub fn assert(diagnostic: &lsp_types::Diagnostic) -> Option<Quickfix> {
let is_error = diagnostic.severity == Some(lsp_types::DiagnosticSeverity::ERROR);
if is_error && match_code(diagnostic, UNKNOWN_VARIABLE) {
return Some(Quickfix::UnknownVariable);
}
if is_error && match_code(diagnostic, UNKNOWN_MODULE) {
todo!()
}
None
}
pub fn unknown_variable(
compiler: &LspProject,
text_document: &lsp_types::TextDocumentIdentifier,
diagnostic: &lsp_types::Diagnostic,
) -> Vec<lsp_types::CodeAction> {
let mut actions = Vec::new();
if let Some(parsed_document) = edits::parse_document(text_document) {
if let Some(serde_json::Value::String(ref var_name)) = diagnostic.data {
for module in compiler.project.modules() {
let mut changes = HashMap::new();
if module.ast.has_definition(var_name) {
if let Some((title, edit)) = parsed_document.import(&module, Some(var_name)) {
changes.insert(text_document.uri.clone(), vec![edit]);
actions.push(lsp_types::CodeAction {
title,
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
is_preferred: Some(true),
disabled: None,
data: None,
command: None,
edit: Some(lsp_types::WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
}),
});
}
}
}
}
}
actions
}

View File

@ -33,9 +33,9 @@ use miette::Diagnostic;
use crate::{ use crate::{
cast::{cast_notification, cast_request}, cast::{cast_notification, cast_request},
edits,
error::Error as ServerError, error::Error as ServerError,
line_numbers::LineNumbers, line_numbers::LineNumbers,
quickfix::{self, Quickfix},
utils::{ utils::{
path_to_uri, span_to_lsp_range, text_edit_replace, uri_to_module_name, path_to_uri, span_to_lsp_range, text_edit_replace, uri_to_module_name,
COMPILING_PROGRESS_TOKEN, CREATE_COMPILING_PROGRESS_TOKEN, COMPILING_PROGRESS_TOKEN, CREATE_COMPILING_PROGRESS_TOKEN,
@ -44,11 +44,9 @@ use crate::{
use self::lsp_project::LspProject; use self::lsp_project::LspProject;
mod lsp_project; pub mod lsp_project;
pub mod telemetry; pub mod telemetry;
const UNKNOWN_VARIABLE: &str = "aiken::check::unknown::variable";
#[allow(dead_code)] #[allow(dead_code)]
pub struct Server { pub struct Server {
// Project root directory // Project root directory
@ -331,45 +329,32 @@ impl Server {
} }
CodeActionRequest::METHOD => { CodeActionRequest::METHOD => {
let params = let mut actions = Vec::new();
cast_request::<CodeActionRequest>(request).expect("cast code action request");
// Identify any diagnostic which refers to an unknown variable. In which case, we if let Some(ref compiler) = self.compiler {
// might want to provide some imports suggestion. let params = cast_request::<CodeActionRequest>(request)
let unknown_variables = params .expect("cast code action request");
.context
.diagnostics
.into_iter()
.filter(|diagnostic| {
let is_error =
diagnostic.severity == Some(lsp_types::DiagnosticSeverity::ERROR);
let is_unknown_variable = diagnostic.code
== Some(lsp_types::NumberOrString::String(
UNKNOWN_VARIABLE.to_string(),
));
is_error && is_unknown_variable for diagnostic in params.context.diagnostics.iter() {
}) match quickfix::assert(diagnostic) {
.collect::<Vec<lsp_types::Diagnostic>>(); Some(Quickfix::UnknownVariable) => {
let quickfixes = quickfix::unknown_variable(
match unknown_variables.first() { compiler,
Some(diagnostic) => Ok(lsp_server::Response { &params.text_document,
id, diagnostic,
error: None, );
result: Some(serde_json::to_value( actions.extend(quickfixes);
self.quickfix_unknown_variable(
params.text_document,
diagnostic.to_owned(),
)
.unwrap_or(vec![]),
)?),
}),
None => Ok(lsp_server::Response {
id,
error: None,
result: Some(serde_json::Value::Null),
}),
} }
None => (),
}
}
}
Ok(lsp_server::Response {
id,
error: None,
result: Some(serde_json::to_value(actions)?),
})
} }
unsupported => Err(ServerError::UnsupportedLspRequest { unsupported => Err(ServerError::UnsupportedLspRequest {
@ -403,46 +388,6 @@ impl Server {
} }
} }
fn quickfix_unknown_variable(
&self,
text_document: lsp_types::TextDocumentIdentifier,
diagnostic: lsp_types::Diagnostic,
) -> Option<Vec<lsp_types::CodeAction>> {
let compiler = self.compiler.as_ref()?;
let parsed_document = edits::parse_document(&text_document)?;
let mut actions = Vec::new();
if let Some(serde_json::Value::String(ref var_name)) = diagnostic.data {
for module in compiler.project.modules() {
let mut changes = HashMap::new();
if module.ast.has_definition(var_name) {
if let Some((title, edit)) = parsed_document.import(&module, Some(var_name)) {
changes.insert(text_document.uri.clone(), vec![edit]);
actions.push(lsp_types::CodeAction {
title,
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
is_preferred: Some(true),
disabled: None,
data: None,
command: None,
edit: Some(lsp_types::WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
}),
});
}
}
}
}
Some(actions)
}
fn completion_for_import(&self) -> Option<Vec<lsp_types::CompletionItem>> { fn completion_for_import(&self) -> Option<Vec<lsp_types::CompletionItem>> {
let compiler = self.compiler.as_ref()?; let compiler = self.compiler.as_ref()?;