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;
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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::{
|
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 {
|
¶ms.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()?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue