Implement quickfix for 'UnknownModule'.
This commit is contained in:
parent
e48ac6b592
commit
c550b4766d
|
@ -964,7 +964,6 @@ impl ExtraData for Error {
|
||||||
| Error::UnexpectedLabeledArg { .. }
|
| Error::UnexpectedLabeledArg { .. }
|
||||||
| Error::UnexpectedLabeledArgInPattern { .. }
|
| Error::UnexpectedLabeledArgInPattern { .. }
|
||||||
| Error::UnknownLabels { .. }
|
| Error::UnknownLabels { .. }
|
||||||
| Error::UnknownModule { .. }
|
|
||||||
| Error::UnknownModuleField { .. }
|
| Error::UnknownModuleField { .. }
|
||||||
| Error::UnknownModuleType { .. }
|
| Error::UnknownModuleType { .. }
|
||||||
| Error::UnknownModuleValue { .. }
|
| Error::UnknownModuleValue { .. }
|
||||||
|
@ -975,7 +974,9 @@ impl ExtraData for Error {
|
||||||
| Error::UpdateMultiConstructorType { .. }
|
| Error::UpdateMultiConstructorType { .. }
|
||||||
| Error::ValidatorImported { .. }
|
| Error::ValidatorImported { .. }
|
||||||
| Error::ValidatorMustReturnBool { .. } => None,
|
| Error::ValidatorMustReturnBool { .. } => None,
|
||||||
Error::UnknownVariable { name, .. } => Some(name.clone()),
|
Error::UnknownVariable { name, .. } | Error::UnknownModule { name, .. } => {
|
||||||
|
Some(name.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub struct ParsedDocument {
|
||||||
line_numbers: LineNumbers,
|
line_numbers: LineNumbers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type AnnotatedEdit = (String, lsp_types::TextEdit);
|
||||||
|
|
||||||
/// Parse the target document as an 'UntypedModule' alongside its line numbers. This is useful in
|
/// Parse the target document as an 'UntypedModule' alongside its line numbers. This is useful in
|
||||||
/// case we need to manipulate the AST for a quickfix.
|
/// case we need to manipulate the AST for a quickfix.
|
||||||
pub fn parse_document(document: &lsp_types::TextDocumentIdentifier) -> Option<ParsedDocument> {
|
pub fn parse_document(document: &lsp_types::TextDocumentIdentifier) -> Option<ParsedDocument> {
|
||||||
|
@ -46,7 +48,7 @@ impl ParsedDocument {
|
||||||
&self,
|
&self,
|
||||||
import: &CheckedModule,
|
import: &CheckedModule,
|
||||||
unqualified: Option<&str>,
|
unqualified: Option<&str>,
|
||||||
) -> Option<(String, lsp_types::TextEdit)> {
|
) -> Option<AnnotatedEdit> {
|
||||||
let import_path = import.name.split('/').collect_vec();
|
let import_path = import.name.split('/').collect_vec();
|
||||||
|
|
||||||
let mut last_import = None;
|
let mut last_import = None;
|
||||||
|
@ -115,7 +117,7 @@ impl ParsedDocument {
|
||||||
import: &CheckedModule,
|
import: &CheckedModule,
|
||||||
unqualified: &str,
|
unqualified: &str,
|
||||||
location: Span,
|
location: Span,
|
||||||
) -> (String, lsp_types::TextEdit) {
|
) -> AnnotatedEdit {
|
||||||
let title = format!(
|
let title = format!(
|
||||||
"Insert new unqualified import '{}' to {}",
|
"Insert new unqualified import '{}' to {}",
|
||||||
unqualified, import.name
|
unqualified, import.name
|
||||||
|
@ -135,7 +137,7 @@ impl ParsedDocument {
|
||||||
import: &CheckedModule,
|
import: &CheckedModule,
|
||||||
unqualified: &str,
|
unqualified: &str,
|
||||||
location: Span,
|
location: Span,
|
||||||
) -> (String, lsp_types::TextEdit) {
|
) -> AnnotatedEdit {
|
||||||
let title = format!(
|
let title = format!(
|
||||||
"Add new unqualified import '{}' to {}",
|
"Add new unqualified import '{}' to {}",
|
||||||
unqualified, import.name
|
unqualified, import.name
|
||||||
|
@ -155,7 +157,7 @@ impl ParsedDocument {
|
||||||
import: &CheckedModule,
|
import: &CheckedModule,
|
||||||
unqualified: Option<&str>,
|
unqualified: Option<&str>,
|
||||||
location: Option<Span>,
|
location: Option<Span>,
|
||||||
) -> (String, lsp_types::TextEdit) {
|
) -> AnnotatedEdit {
|
||||||
let import_line = format!(
|
let import_line = format!(
|
||||||
"use {}{}",
|
"use {}{}",
|
||||||
import.name,
|
import.name,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::{edits, server::lsp_project::LspProject};
|
use crate::{
|
||||||
|
edits::{self, AnnotatedEdit, ParsedDocument},
|
||||||
|
server::lsp_project::LspProject,
|
||||||
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const UNKNOWN_VARIABLE: &str = "aiken::check::unknown::variable";
|
const UNKNOWN_VARIABLE: &str = "aiken::check::unknown::variable";
|
||||||
|
@ -7,60 +10,101 @@ const UNKNOWN_MODULE: &str = "aiken::check::unknown::module";
|
||||||
|
|
||||||
/// Errors for which we can provide quickfixes
|
/// Errors for which we can provide quickfixes
|
||||||
pub enum Quickfix {
|
pub enum Quickfix {
|
||||||
UnknownVariable,
|
UnknownIdentifier,
|
||||||
|
UnknownModule,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_code(diagnostic: &lsp_types::Diagnostic, expected: &str) -> bool {
|
fn match_code(diagnostic: &lsp_types::Diagnostic, expected: &str) -> bool {
|
||||||
diagnostic.code == Some(lsp_types::NumberOrString::String(expected.to_string()))
|
diagnostic.code == Some(lsp_types::NumberOrString::String(expected.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assert whether a diagnostic can be automatically fixed. Note that diagnostics often comes in
|
||||||
|
/// two severities, an error and hint; so we must be careful only addressing errors.
|
||||||
pub fn assert(diagnostic: &lsp_types::Diagnostic) -> Option<Quickfix> {
|
pub fn assert(diagnostic: &lsp_types::Diagnostic) -> Option<Quickfix> {
|
||||||
let is_error = diagnostic.severity == Some(lsp_types::DiagnosticSeverity::ERROR);
|
let is_error = diagnostic.severity == Some(lsp_types::DiagnosticSeverity::ERROR);
|
||||||
|
|
||||||
if is_error && match_code(diagnostic, UNKNOWN_VARIABLE) {
|
if is_error && match_code(diagnostic, UNKNOWN_VARIABLE) {
|
||||||
return Some(Quickfix::UnknownVariable);
|
return Some(Quickfix::UnknownIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_error && match_code(diagnostic, UNKNOWN_MODULE) {
|
if is_error && match_code(diagnostic, UNKNOWN_MODULE) {
|
||||||
todo!()
|
return Some(Quickfix::UnknownModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unknown_variable(
|
pub fn quickfix(
|
||||||
compiler: &LspProject,
|
compiler: &LspProject,
|
||||||
text_document: &lsp_types::TextDocumentIdentifier,
|
text_document: &lsp_types::TextDocumentIdentifier,
|
||||||
diagnostic: &lsp_types::Diagnostic,
|
diagnostic: &lsp_types::Diagnostic,
|
||||||
|
quickfix: &Quickfix,
|
||||||
) -> Vec<lsp_types::CodeAction> {
|
) -> Vec<lsp_types::CodeAction> {
|
||||||
let mut actions = Vec::new();
|
let mut actions = Vec::new();
|
||||||
|
|
||||||
if let Some(parsed_document) = edits::parse_document(text_document) {
|
if let Some(ref parsed_document) = edits::parse_document(text_document) {
|
||||||
if let Some(serde_json::Value::String(ref var_name)) = diagnostic.data {
|
if let Some(serde_json::Value::String(ref data)) = diagnostic.data {
|
||||||
for module in compiler.project.modules() {
|
let edits = match quickfix {
|
||||||
|
Quickfix::UnknownIdentifier => unknown_identifier(compiler, parsed_document, data),
|
||||||
|
Quickfix::UnknownModule => unknown_module(compiler, parsed_document, data),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (title, edit) in edits.into_iter() {
|
||||||
let mut changes = HashMap::new();
|
let mut changes = HashMap::new();
|
||||||
if module.ast.has_definition(var_name) {
|
changes.insert(text_document.uri.clone(), vec![edit]);
|
||||||
if let Some((title, edit)) = parsed_document.import(&module, Some(var_name)) {
|
actions.push(lsp_types::CodeAction {
|
||||||
changes.insert(text_document.uri.clone(), vec![edit]);
|
title,
|
||||||
actions.push(lsp_types::CodeAction {
|
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
|
||||||
title,
|
diagnostics: Some(vec![diagnostic.clone()]),
|
||||||
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
|
is_preferred: Some(true),
|
||||||
diagnostics: Some(vec![diagnostic.clone()]),
|
disabled: None,
|
||||||
is_preferred: Some(true),
|
data: None,
|
||||||
disabled: None,
|
command: None,
|
||||||
data: None,
|
edit: Some(lsp_types::WorkspaceEdit {
|
||||||
command: None,
|
changes: Some(changes),
|
||||||
edit: Some(lsp_types::WorkspaceEdit {
|
document_changes: None,
|
||||||
changes: Some(changes),
|
change_annotations: None,
|
||||||
document_changes: None,
|
}),
|
||||||
change_annotations: None,
|
});
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actions
|
actions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unknown_identifier(
|
||||||
|
compiler: &LspProject,
|
||||||
|
parsed_document: &ParsedDocument,
|
||||||
|
var_name: &str,
|
||||||
|
) -> Vec<AnnotatedEdit> {
|
||||||
|
let mut edits = Vec::new();
|
||||||
|
|
||||||
|
for module in compiler.project.modules() {
|
||||||
|
if module.ast.has_definition(var_name) {
|
||||||
|
if let Some(edit) = parsed_document.import(&module, Some(var_name)) {
|
||||||
|
edits.push(edit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edits
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unknown_module(
|
||||||
|
compiler: &LspProject,
|
||||||
|
parsed_document: &ParsedDocument,
|
||||||
|
module_name: &str,
|
||||||
|
) -> Vec<AnnotatedEdit> {
|
||||||
|
let mut edits = Vec::new();
|
||||||
|
|
||||||
|
for module in compiler.project.modules() {
|
||||||
|
if module.name.ends_with(module_name) {
|
||||||
|
if let Some(edit) = parsed_document.import(&module, None) {
|
||||||
|
edits.push(edit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edits
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ use crate::{
|
||||||
cast::{cast_notification, cast_request},
|
cast::{cast_notification, cast_request},
|
||||||
error::Error as ServerError,
|
error::Error as ServerError,
|
||||||
line_numbers::LineNumbers,
|
line_numbers::LineNumbers,
|
||||||
quickfix::{self, Quickfix},
|
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,
|
||||||
|
@ -336,16 +336,14 @@ impl Server {
|
||||||
.expect("cast code action request");
|
.expect("cast code action request");
|
||||||
|
|
||||||
for diagnostic in params.context.diagnostics.iter() {
|
for diagnostic in params.context.diagnostics.iter() {
|
||||||
match quickfix::assert(diagnostic) {
|
if let Some(strategy) = quickfix::assert(diagnostic) {
|
||||||
Some(Quickfix::UnknownVariable) => {
|
let quickfixes = quickfix::quickfix(
|
||||||
let quickfixes = quickfix::unknown_variable(
|
compiler,
|
||||||
compiler,
|
¶ms.text_document,
|
||||||
¶ms.text_document,
|
diagnostic,
|
||||||
diagnostic,
|
&strategy,
|
||||||
);
|
);
|
||||||
actions.extend(quickfixes);
|
actions.extend(quickfixes);
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue