From 68b1f8bb21b7391f1dd360f73d50a250a934c53b Mon Sep 17 00:00:00 2001 From: He1pa <56333845+He1pa@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:28:13 +0800 Subject: [PATCH] arch: migrate lsp newline complete to new sema model (#889) * arch: migrate lsp newline complete to new sema model Signed-off-by: he1pa <18012015693@163.com> * test: sort result in ut Signed-off-by: he1pa <18012015693@163.com> --------- Signed-off-by: he1pa <18012015693@163.com> --- kclvm/tools/src/LSP/src/completion.rs | 101 ++++++++++-------- kclvm/tools/src/LSP/src/request.rs | 17 +-- .../newline/docstring_newline.k | 3 +- .../completion_test/newline/newline.k | 4 +- 4 files changed, 73 insertions(+), 52 deletions(-) diff --git a/kclvm/tools/src/LSP/src/completion.rs b/kclvm/tools/src/LSP/src/completion.rs index 9b46a90e3..07eb148f4 100644 --- a/kclvm/tools/src/LSP/src/completion.rs +++ b/kclvm/tools/src/LSP/src/completion.rs @@ -85,7 +85,7 @@ pub(crate) fn completion( Some(c) => match c { '.' => completion_dot(program, pos, prog_scope, gs), '=' | ':' => completion_assign(pos, gs), - '\n' => completion_newline(program, pos, prog_scope), + '\n' => completion_newline(program, pos, prog_scope, gs), _ => None, }, None => { @@ -304,51 +304,66 @@ fn completion_assign(pos: &KCLPos, gs: &GlobalState) -> Option Option { let mut completions: IndexSet = IndexSet::new(); - let pos = &KCLPos { - filename: pos.filename.clone(), - line: pos.line - 1, - column: pos.column, - }; - match program.pos_to_stmt(pos) { - Some(node) => { - let end_pos = node.get_end_pos(); - if let Some((node, schema_expr)) = is_in_schema_expr(program, &end_pos) { - let schema_def = find_def(node, &schema_expr.name.get_end_pos(), prog_scope); - if let Some(schema) = schema_def { - if let Definition::Object(obj, _) = schema { - let schema_type = obj.ty.into_schema_type(); - completions.extend(schema_type.attrs.iter().map(|(name, attr)| { - KCLCompletionItem { - label: name.clone(), - detail: Some(format!("{}: {}", name, attr.ty.ty_str())), - documentation: attr.doc.clone(), - kind: Some(KCLCompletionItemKind::SchemaAttr), + if let Some((doc, schema)) = is_in_docstring(program, &pos) { + let doc = parse_doc_string(&doc.node); + if doc.summary.is_empty() && doc.attrs.len() == 0 && doc.examples.len() == 0 { + // empty docstring, provide total completion + let doc_parsed = Doc::new_from_schema_stmt(&schema); + let label = doc_parsed.to_doc_string(); + // generate docstring from doc + completions.insert(KCLCompletionItem { + label, + detail: Some("generate docstring".to_string()), + documentation: Some(format!("docstring for {}", schema.name.node.clone())), + kind: Some(KCLCompletionItemKind::Doc), + }); + } + return Some(into_completion_items(&completions).into()); + } + + // Complete schema attr when input newline in schema + if let Some(scope) = gs.look_up_scope(pos) { + if let Some(defs) = gs.get_all_defs_in_scope(scope) { + for symbol_ref in defs { + match gs.get_symbols().get_symbol(symbol_ref) { + Some(def) => { + let sema_info = def.get_sema_info(); + let name = def.get_name(); + match symbol_ref.get_kind() { + kclvm_sema::core::symbol::SymbolKind::Attribute => { + completions.insert(KCLCompletionItem { + label: name.clone(), + detail: match &sema_info.ty { + Some(ty) => Some(format!("{}: {}", name, ty.ty_str())), + None => None, + }, + documentation: match &sema_info.doc { + Some(doc) => { + if doc.is_empty() { + None + } else { + Some(doc.clone()) + } + } + None => None, + }, + kind: Some(KCLCompletionItemKind::SchemaAttr), + }); } - })); + _ => {} + } } - } - } else if let Some((doc, schema)) = is_in_docstring(program, &pos) { - let doc = parse_doc_string(&doc.node); - if doc.summary.is_empty() && doc.attrs.len() == 0 && doc.examples.len() == 0 { - // empty docstring, provide total completion - let doc_parsed = Doc::new_from_schema_stmt(&schema); - let label = doc_parsed.to_doc_string(); - // generate docstring from doc - completions.insert(KCLCompletionItem { - label, - detail: Some("generate docstring".to_string()), - documentation: Some(format!("docstring for {}", schema.name.node.clone())), - kind: Some(KCLCompletionItemKind::Doc), - }); + None => {} } } } - None => {} } + Some(into_completion_items(&completions).into()) } @@ -1054,11 +1069,12 @@ mod tests { column: Some(4), }; - let got = completion(Some('\n'), &program, &pos, &prog_scope, &gs).unwrap(); - match got { + let mut got = completion(Some('\n'), &program, &pos, &prog_scope, &gs).unwrap(); + match &mut got { CompletionResponse::Array(arr) => { + arr.sort_by(|a, b| a.label.cmp(&b.label)); assert_eq!( - arr[0], + arr[1], CompletionItem { label: "c".to_string(), kind: Some(CompletionItemKind::FIELD), @@ -1084,9 +1100,10 @@ mod tests { column: Some(4), }; - let got = completion(Some('\n'), &program, &pos, &prog_scope, &gs).unwrap(); - match got { + let mut got = completion(Some('\n'), &program, &pos, &prog_scope, &gs).unwrap(); + match &mut got { CompletionResponse::Array(arr) => { + arr.sort_by(|a, b| a.label.cmp(&b.label)); assert_eq!( arr[0], CompletionItem { diff --git a/kclvm/tools/src/LSP/src/request.rs b/kclvm/tools/src/LSP/src/request.rs index 3ac10c9b1..f019fd608 100644 --- a/kclvm/tools/src/LSP/src/request.rs +++ b/kclvm/tools/src/LSP/src/request.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Ok}; use crossbeam_channel::Sender; +use kclvm_ast::pos::GetPos; use kclvm_sema::info::is_valid_kcl_name; use lsp_types::{Location, TextEdit}; use ra_ap_vfs::VfsPath; @@ -17,6 +18,7 @@ use crate::{ goto_def::goto_definition_with_gs, hover, quick_fix, state::{log_message, LanguageServerSnapshot, LanguageServerState, Task}, + util::{parse_param_and_compile, Param}, }; impl LanguageServerState { @@ -194,19 +196,18 @@ pub(crate) fn handle_completion( if !snapshot.verify_request_path(&path.clone().into(), &sender) { return Ok(None); } - let db = snapshot.get_db(&path.clone().into())?; + + let db = + parse_param_and_compile(Param { file: file.clone() }, Some(snapshot.vfs.clone())).unwrap(); + let kcl_pos = kcl_pos(&file, params.text_document_position.position); let completion_trigger_character = params .context .and_then(|ctx| ctx.trigger_character) .and_then(|s| s.chars().next()); - let res = completion( - completion_trigger_character, - &db.prog, - &kcl_pos, - &db.scope, - &db.gs, - ); + + let res = completion(completion_trigger_character, &db.0, &kcl_pos, &db.1, &db.3); + if res.is_none() { log_message("Completion item not found".to_string(), &sender)?; } diff --git a/kclvm/tools/src/LSP/src/test_data/completion_test/newline/docstring_newline.k b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/docstring_newline.k index 2127c9a14..d4bbd0ba2 100644 --- a/kclvm/tools/src/LSP/src/test_data/completion_test/newline/docstring_newline.k +++ b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/docstring_newline.k @@ -1,5 +1,6 @@ schema Server: - """""" + """ + """ name: str workloadType: "Deployment" | "StatefulSet" replica: int = 1 diff --git a/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline.k b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline.k index 45a863849..263155647 100644 --- a/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline.k +++ b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline.k @@ -4,4 +4,6 @@ schema Base: schema Person[b: int](Base): c: int -p1= Person(b){} +p1= Person(b){ + +}