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:
parent
763516eb96
commit
e48ac6b592
|
@ -8,6 +8,7 @@ mod cast;
|
|||
mod edits;
|
||||
pub mod error;
|
||||
mod line_numbers;
|
||||
mod quickfix;
|
||||
pub mod server;
|
||||
mod utils;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -33,9 +33,9 @@ use miette::Diagnostic;
|
|||
|
||||
use crate::{
|
||||
cast::{cast_notification, cast_request},
|
||||
edits,
|
||||
error::Error as ServerError,
|
||||
line_numbers::LineNumbers,
|
||||
quickfix::{self, Quickfix},
|
||||
utils::{
|
||||
path_to_uri, span_to_lsp_range, text_edit_replace, uri_to_module_name,
|
||||
COMPILING_PROGRESS_TOKEN, CREATE_COMPILING_PROGRESS_TOKEN,
|
||||
|
@ -44,11 +44,9 @@ use crate::{
|
|||
|
||||
use self::lsp_project::LspProject;
|
||||
|
||||
mod lsp_project;
|
||||
pub mod lsp_project;
|
||||
pub mod telemetry;
|
||||
|
||||
const UNKNOWN_VARIABLE: &str = "aiken::check::unknown::variable";
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Server {
|
||||
// Project root directory
|
||||
|
@ -331,45 +329,32 @@ impl Server {
|
|||
}
|
||||
|
||||
CodeActionRequest::METHOD => {
|
||||
let params =
|
||||
cast_request::<CodeActionRequest>(request).expect("cast code action request");
|
||||
let mut actions = Vec::new();
|
||||
|
||||
// Identify any diagnostic which refers to an unknown variable. In which case, we
|
||||
// might want to provide some imports suggestion.
|
||||
let unknown_variables = params
|
||||
.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(),
|
||||
));
|
||||
if let Some(ref compiler) = self.compiler {
|
||||
let params = cast_request::<CodeActionRequest>(request)
|
||||
.expect("cast code action request");
|
||||
|
||||
is_error && is_unknown_variable
|
||||
})
|
||||
.collect::<Vec<lsp_types::Diagnostic>>();
|
||||
|
||||
match unknown_variables.first() {
|
||||
Some(diagnostic) => Ok(lsp_server::Response {
|
||||
id,
|
||||
error: None,
|
||||
result: Some(serde_json::to_value(
|
||||
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),
|
||||
}),
|
||||
for diagnostic in params.context.diagnostics.iter() {
|
||||
match quickfix::assert(diagnostic) {
|
||||
Some(Quickfix::UnknownVariable) => {
|
||||
let quickfixes = quickfix::unknown_variable(
|
||||
compiler,
|
||||
¶ms.text_document,
|
||||
diagnostic,
|
||||
);
|
||||
actions.extend(quickfixes);
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(lsp_server::Response {
|
||||
id,
|
||||
error: None,
|
||||
result: Some(serde_json::to_value(actions)?),
|
||||
})
|
||||
}
|
||||
|
||||
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>> {
|
||||
let compiler = self.compiler.as_ref()?;
|
||||
|
||||
|
|
Loading…
Reference in New Issue