diff --git a/kclvm/Cargo.lock b/kclvm/Cargo.lock index 3350ec3a7..b3ca38cc7 100644 --- a/kclvm/Cargo.lock +++ b/kclvm/Cargo.lock @@ -1641,6 +1641,7 @@ dependencies = [ "indexmap 1.9.3", "kclvm-runtime", "kclvm-span", + "serde_json", "termize", "tracing", ] diff --git a/kclvm/error/Cargo.toml b/kclvm/error/Cargo.toml index c0c19466e..ae342ab4e 100644 --- a/kclvm/error/Cargo.toml +++ b/kclvm/error/Cargo.toml @@ -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" diff --git a/kclvm/error/src/diagnostic.rs b/kclvm/error/src/diagnostic.rs index bbcb7f7e8..6c8034c2c 100644 --- a/kclvm/error/src/diagnostic.rs +++ b/kclvm/error/src/diagnostic.rs @@ -5,11 +5,23 @@ use std::hash::Hash; use crate::{ErrorKind, WarningKind}; /// Diagnostic structure. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Diagnostic { pub level: Level, pub messages: Vec, pub code: Option, + pub data: Option, +} + +impl Hash for Diagnostic { + fn hash(&self, state: &mut H) { + self.level.hash(state); + for message in &self.messages { + message.hash(state); + } + self.code.hash(state); + // The `data` field is not included in the hash calculation due to complexity. + } } /// Position describes an arbitrary source position including the filename, @@ -106,18 +118,23 @@ impl Diagnostic { note: Option<&str>, range: Range, code: Option, - suggested_replacement: Option, + suggestions: Option>, ) -> Self { + let data = suggestions.clone().map(|suggs| { + serde_json::json!({ "suggested_replacements": suggs }) + }); + // println!("Data received: {:?}", data); 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: None, // Assuming your Message struct has such a field }], code, + data, // Now includes suggestions if provided } } diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index 5af39db81..1109b6330 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -125,17 +125,31 @@ 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 { + 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>, + ) -> &mut Self { let diag = Diagnostic::new_with_code( Level::Error, msg, None, range, Some(DiagnosticId::Error(E2L23.kind)), - None, + suggestions, ); + // println!("{:?}",suggestions.clone()); self.add_diagnostic(diag); - + self } @@ -165,6 +179,7 @@ impl Handler { level: Level::Error, messages: msgs.to_owned(), code: Some(DiagnosticId::Error(err)), + data: None, }; self.add_diagnostic(diag); @@ -183,6 +198,7 @@ impl Handler { suggested_replacement: None, }], code: Some(DiagnosticId::Suggestions), + data: None, }); }); @@ -208,6 +224,7 @@ impl Handler { level: Level::Warning, messages: msgs.to_owned(), code: Some(DiagnosticId::Warning(warning)), + data: None, }; self.add_diagnostic(diag); diff --git a/kclvm/sema/src/resolver/scope.rs b/kclvm/sema/src/resolver/scope.rs index dcb7a3623..da8ef92ff 100644 --- a/kclvm/sema/src/resolver/scope.rs +++ b/kclvm/sema/src/resolver/scope.rs @@ -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() } diff --git a/kclvm/tools/src/LSP/src/quick_fix.rs b/kclvm/tools/src/LSP/src/quick_fix.rs index 95f54dabd..821345956 100644 --- a/kclvm/tools/src/LSP/src/quick_fix.rs +++ b/kclvm/tools/src/LSP/src/quick_fix.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; -use kclvm_error::{DiagnosticId, WarningKind}; +use kclvm_error::{DiagnosticId, WarningKind, ErrorKind}; use lsp_types::{ - CodeAction, CodeActionKind, CodeActionOrCommand, Diagnostic, NumberOrString, TextEdit, Url, + CodeAction, CodeActionKind, CodeActionOrCommand, Diagnostic, NumberOrString, TextEdit, Url }; +use serde_json::Value; pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec { let mut code_actions: Vec = vec![]; @@ -11,7 +12,30 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec continue, + DiagnosticId::Error(error) => match error { + ErrorKind::CompileError => { + let replacement_text = extract_suggested_replacements(&diag.data).unwrap_or_else(|| "".to_string()); + let mut changes = HashMap::new(); + changes.insert( + uri.clone(), + vec![TextEdit { + range: diag.range, + new_text: replacement_text, + }], + ); + code_actions.push(CodeActionOrCommand::CodeAction(CodeAction { + title: ErrorKind::CompileError.name(), + kind: Some(CodeActionKind::QUICKFIX), + diagnostics: Some(vec![diag.clone()]), + edit: Some(lsp_types::WorkspaceEdit { + changes: Some(changes), + ..Default::default() + }), + ..Default::default() + })) + } + _ => continue, + }, DiagnosticId::Warning(warn) => match warn { WarningKind::UnusedImportWarning => { let mut changes = HashMap::new(); @@ -63,6 +87,23 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec) -> Option { + data.as_ref().and_then(|data| { + match data { + Value::Object(obj) => { + obj.get("suggested_replacements").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 { match code { NumberOrString::Number(_) => None, @@ -70,6 +111,7 @@ pub(crate) fn conver_code_to_kcl_diag_id(code: &NumberOrString) -> Option 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)) } diff --git a/kclvm/tools/src/LSP/src/to_lsp.rs b/kclvm/tools/src/LSP/src/to_lsp.rs index 87e8336d4..d13778ff8 100644 --- a/kclvm/tools/src/LSP/src/to_lsp.rs +++ b/kclvm/tools/src/LSP/src/to_lsp.rs @@ -97,12 +97,22 @@ pub fn kcl_diag_to_lsp_diags(diag: &KCLDiagnostic, file_name: &str) -> Vec