Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lfx pre test lsp quick fix #1038

Merged
merged 15 commits into from
Feb 19, 2024
1 change: 1 addition & 0 deletions kclvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions kclvm/error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ atty = "0.2"
annotate-snippets = { version = "0.9.2", default-features = false, features = ["color"] }
termize = "0.1.1"
indexmap = "1.0"
serde_json = "1.0.86"
6 changes: 3 additions & 3 deletions kclvm/error/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,16 @@ impl Diagnostic {
note: Option<&str>,
range: Range,
code: Option<DiagnosticId>,
suggested_replacement: Option<String>,
suggestions: Option<Vec<String>>,
) -> Self {
Diagnostic {
level,
messages: vec![Message {
range,
style: Style::LineAndColumn,
message: message.to_string(),
note: note.map(|s| s.to_string()),
suggested_replacement,
note: note.map(String::from),
suggested_replacement: suggestions.and_then(|v| v.into_iter().next()),
}],
code,
}
Expand Down
11 changes: 10 additions & 1 deletion kclvm/error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,22 @@ impl Handler {

/// Construct a type error and put it into the handler diagnostic buffer
pub fn add_compile_error(&mut self, msg: &str, range: Range) -> &mut Self {
self.add_compile_error_with_suggestions(msg, range, None)
}

pub fn add_compile_error_with_suggestions(
&mut self,
msg: &str,
range: Range,
suggestions: Option<Vec<String>>,
) -> &mut Self {
let diag = Diagnostic::new_with_code(
Level::Error,
msg,
None,
range,
Some(DiagnosticId::Error(E2L23.kind)),
None,
suggestions,
);
self.add_diagnostic(diag);

Expand Down
3 changes: 2 additions & 1 deletion kclvm/sema/src/resolver/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,14 @@ impl<'ctx> Resolver<'ctx> {
if suggs.len() > 0 {
suggestion = format!(", did you mean '{:?}'?", suggs);
}
self.handler.add_compile_error(
self.handler.add_compile_error_with_suggestions(
&format!(
"name '{}' is not defined{}",
name.replace('@', ""),
suggestion
),
range,
Some(suggs.clone()),
);
self.any_ty()
}
Expand Down
52 changes: 50 additions & 2 deletions kclvm/tools/src/LSP/src/quick_fix.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,49 @@
use std::collections::HashMap;

use kclvm_error::{DiagnosticId, WarningKind};
use kclvm_error::{DiagnosticId, ErrorKind, WarningKind};
use lsp_types::{
CodeAction, CodeActionKind, CodeActionOrCommand, Diagnostic, NumberOrString, TextEdit, Url,
};
use serde_json::Value;

pub(crate) fn quick_fix(uri: &Url, diags: &Vec<Diagnostic>) -> Vec<lsp_types::CodeActionOrCommand> {
let mut code_actions: Vec<lsp_types::CodeActionOrCommand> = vec![];
for diag in diags {
if let Some(code) = &diag.code {
if let Some(id) = conver_code_to_kcl_diag_id(code) {
match id {
DiagnosticId::Error(_) => continue,
DiagnosticId::Error(error) => match error {
ErrorKind::CompileError => {
if let Some(replacement_text) =
extract_suggested_replacement(&diag.data)
{
let mut changes = HashMap::new();
changes.insert(
uri.clone(),
vec![TextEdit {
range: diag.range,
new_text: replacement_text.clone(),
}],
);
code_actions.push(CodeActionOrCommand::CodeAction(CodeAction {
title: format!(
"a local variable with a similar name exists: `{}`",
replacement_text
),
kind: Some(CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diag.clone()]),
edit: Some(lsp_types::WorkspaceEdit {
changes: Some(changes),
..Default::default()
}),
..Default::default()
}))
} else {
continue;
}
}
_ => continue,
},
DiagnosticId::Warning(warn) => match warn {
WarningKind::UnusedImportWarning => {
let mut changes = HashMap::new();
Expand Down Expand Up @@ -63,13 +95,29 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec<Diagnostic>) -> Vec<lsp_types::Co
code_actions
}

fn extract_suggested_replacement(data: &Option<Value>) -> Option<String> {
data.as_ref().and_then(|data| match data {
Value::Object(obj) => obj.get("suggested_replacement").and_then(|val| match val {
Value::String(s) => Some(s.clone()),
Value::Array(arr) if !arr.is_empty() => arr
.iter()
.filter_map(|v| v.as_str())
.next()
.map(String::from),
_ => None,
}),
_ => None,
})
}

pub(crate) fn conver_code_to_kcl_diag_id(code: &NumberOrString) -> Option<DiagnosticId> {
match code {
NumberOrString::Number(_) => None,
NumberOrString::String(code) => match code.as_str() {
"CompilerWarning" => Some(DiagnosticId::Warning(WarningKind::CompilerWarning)),
"UnusedImportWarning" => Some(DiagnosticId::Warning(WarningKind::UnusedImportWarning)),
"ReimportWarning" => Some(DiagnosticId::Warning(WarningKind::ReimportWarning)),
"CompileError" => Some(DiagnosticId::Error(ErrorKind::CompileError)),
"ImportPositionWarning" => {
Some(DiagnosticId::Warning(WarningKind::ImportPositionWarning))
}
Expand Down
2 changes: 2 additions & 0 deletions kclvm/tools/src/LSP/src/test_data/diagnostics.k
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ c: Person = Person {

d = 1
d = 2
number = 2
count = nu
18 changes: 17 additions & 1 deletion kclvm/tools/src/LSP/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ fn build_lsp_diag(
severity: Option<DiagnosticSeverity>,
related_info: Vec<(String, (u32, u32, u32, u32), String)>,
code: Option<NumberOrString>,
data: Option<serde_json::Value>,
) -> Diagnostic {
let related_information = if related_info.is_empty() {
None
Expand Down Expand Up @@ -184,7 +185,7 @@ fn build_lsp_diag(
message,
related_information,
tags: None,
data: None,
data,
}
}

Expand All @@ -201,13 +202,15 @@ fn build_expect_diags() -> Vec<Diagnostic> {
Some(DiagnosticSeverity::ERROR),
vec![],
Some(NumberOrString::String("InvalidSyntax".to_string())),
None,
),
build_lsp_diag(
(0, 0, 0, 10),
"pkgpath abc not found in the program".to_string(),
Some(DiagnosticSeverity::ERROR),
vec![],
Some(NumberOrString::String("CannotFindModule".to_string())),
None,
),
build_lsp_diag(
(0, 0, 0, 10),
Expand All @@ -218,6 +221,7 @@ fn build_expect_diags() -> Vec<Diagnostic> {
Some(DiagnosticSeverity::ERROR),
vec![],
Some(NumberOrString::String("CannotFindModule".to_string())),
None,
),
build_lsp_diag(
(8, 0, 8, 1),
Expand All @@ -229,6 +233,7 @@ fn build_expect_diags() -> Vec<Diagnostic> {
"The variable 'd' is declared here".to_string(),
)],
Some(NumberOrString::String("ImmutableError".to_string())),
None,
),
build_lsp_diag(
(7, 0, 7, 1),
Expand All @@ -240,20 +245,31 @@ fn build_expect_diags() -> Vec<Diagnostic> {
"Can not change the value of 'd', because it was declared immutable".to_string(),
)],
Some(NumberOrString::String("ImmutableError".to_string())),
None,
),
build_lsp_diag(
(2, 0, 2, 1),
"expected str, got int(1)".to_string(),
Some(DiagnosticSeverity::ERROR),
vec![],
Some(NumberOrString::String("TypeError".to_string())),
None,
),
build_lsp_diag(
(10, 8, 10, 10),
"name 'nu' is not defined, did you mean '[\"number\", \"n\", \"num\"]'?".to_string(),
Some(DiagnosticSeverity::ERROR),
vec![],
Some(NumberOrString::String("CompileError".to_string())),
Some(serde_json::json!({ "suggested_replacement": ["number"] })),
),
build_lsp_diag(
(0, 0, 0, 10),
"Module 'abc' imported but unused".to_string(),
Some(DiagnosticSeverity::WARNING),
vec![],
Some(NumberOrString::String("UnusedImportWarning".to_string())),
None,
),
];
expected_diags
Expand Down
16 changes: 13 additions & 3 deletions kclvm/tools/src/LSP/src/to_lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use kclvm_error::Message;
use kclvm_error::Position as KCLPos;
use lsp_types::*;
use ra_ap_vfs::FileId;
use serde_json::json;

use crate::state::LanguageServerSnapshot;
use std::{
Expand Down Expand Up @@ -43,6 +44,12 @@ fn kcl_msg_to_lsp_diags(
let start_position = lsp_pos(&range.0);
let end_position = lsp_pos(&range.1);

let data = msg
.suggested_replacement
.as_ref()
.filter(|s| !s.is_empty())
.map(|s| json!({ "suggested_replacement": [s] }));

let related_information = if related_msg.is_empty() {
None
} else {
Expand Down Expand Up @@ -72,7 +79,7 @@ fn kcl_msg_to_lsp_diags(
message: msg.message.clone(),
related_information,
tags: None,
data: None,
data,
}
}

Expand All @@ -97,12 +104,15 @@ pub fn kcl_diag_to_lsp_diags(diag: &KCLDiagnostic, file_name: &str) -> Vec<Diagn
} else {
None
};
diags.push(kcl_msg_to_lsp_diags(

let lsp_diag = kcl_msg_to_lsp_diags(
msg,
kcl_err_level_to_severity(diag.level),
related_msg,
code,
))
);

diags.push(lsp_diag);
}
}
diags
Expand Down