From 4cda3353fe1a263eae89d8c78659ec804236401e Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Fri, 17 May 2024 19:40:42 +0530 Subject: [PATCH] feat: syntax highlighting for hover content Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 kcl formatting Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 updated test cases Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 updated tests Signed-off-by: shruti2522 updated tests Signed-off-by: shruti2522 test check Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 add markup Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 resolved func formatting error Signed-off-by: shruti2522 updated test cases Signed-off-by: shruti2522 updated tests Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 updated test cases for tests.rs Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 syntax highlighting with LanguageString Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 updated tests Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 updated test case Signed-off-by: shruti2522 updated tests.rs Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 fixed ci Signed-off-by: shruti2522 add pkgpath without override Signed-off-by: shruti2522 updated completion.rs Signed-off-by: shruti2522 updated tests.rs Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 updated doc style Signed-off-by: shruti2522 fix ci Signed-off-by: shruti2522 updated comment Signed-off-by: shruti2522 added indentation for attrs Signed-off-by: shruti2522 fix ci Signed-off-by: shruti2522 merge schema attr docs Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 update hover def comment Signed-off-by: shruti2522 updated tests Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 fix ci Signed-off-by: shruti2522 render doc at end Signed-off-by: shruti2522 fix ci Signed-off-by: shruti2522 removed additional def Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 fix ci Signed-off-by: shruti2522 fix dict_key_in_schema test Signed-off-by: shruti2522 merged rest_sign and attr Signed-off-by: shruti2522 fixed ci Signed-off-by: shruti2522 fmt check Signed-off-by: shruti2522 feat: syntax highlighting for hover content Signed-off-by: shruti2522 --- kclvm/sema/src/ty/mod.rs | 15 +-- kclvm/tools/src/LSP/src/completion.rs | 8 +- kclvm/tools/src/LSP/src/hover.rs | 157 ++++++++++++++++---------- kclvm/tools/src/LSP/src/tests.rs | 57 +++++++--- 4 files changed, 148 insertions(+), 89 deletions(-) diff --git a/kclvm/sema/src/ty/mod.rs b/kclvm/sema/src/ty/mod.rs index ad47f5262..23532cfa9 100644 --- a/kclvm/sema/src/ty/mod.rs +++ b/kclvm/sema/src/ty/mod.rs @@ -293,7 +293,7 @@ impl SchemaType { } } - pub fn schema_ty_signature_str(&self) -> String { + pub fn schema_ty_signature_str(&self) -> (String, String) { let base: String = if let Some(base) = &self.base { format!("({})", base.name) } else { @@ -312,17 +312,10 @@ impl SchemaType { .join(", ") ) }; - let params_str = if !params.is_empty() && !base.is_empty() { - format!("\\{}{}", params, base) - } else if !params.is_empty() { - format!("{}", params) - } else if !base.is_empty() { - format!("{}", base) - } else { - "".to_string() - }; - format!("{}\n\nschema {}{}", self.pkgpath, self.name, params_str) + let rest_sign = format!("schema {}{}{}:", self.name, params, base); + + (self.pkgpath.clone(), rest_sign) } } diff --git a/kclvm/tools/src/LSP/src/completion.rs b/kclvm/tools/src/LSP/src/completion.rs index bc43309a9..29bf3aa71 100644 --- a/kclvm/tools/src/LSP/src/completion.rs +++ b/kclvm/tools/src/LSP/src/completion.rs @@ -490,7 +490,8 @@ fn schema_ty_to_value_complete_item(schema_ty: &SchemaType) -> KCLCompletionItem ); let detail = { let mut details = vec![]; - details.push(schema_ty.schema_ty_signature_str()); + let (pkgpath, rest_sign) = schema_ty.schema_ty_signature_str(); + details.push(format!("{}\n\n{}", pkgpath, rest_sign)); details.push("Attributes:".to_string()); for (name, attr) in &schema_ty.attrs { details.push(format!( @@ -543,7 +544,8 @@ fn schema_ty_to_value_complete_item(schema_ty: &SchemaType) -> KCLCompletionItem fn schema_ty_to_type_complete_item(schema_ty: &SchemaType) -> KCLCompletionItem { let detail = { let mut details = vec![]; - details.push(schema_ty.schema_ty_signature_str()); + let (pkgpath, rest_sign) = schema_ty.schema_ty_signature_str(); + details.push(format!("{}\n\n{}", pkgpath, rest_sign)); details.push("Attributes:".to_string()); for (name, attr) in &schema_ty.attrs { details.push(format!( @@ -1259,7 +1261,7 @@ mod tests { label: "Person(b){}".to_string(), kind: Some(CompletionItemKind::CLASS), detail: Some( - "__main__\n\nschema Person\\[b: int](Base)\nAttributes:\nc: int" + "__main__\n\nschema Person[b: int](Base):\nAttributes:\nc: int" .to_string() ), documentation: Some(lsp_types::Documentation::String("".to_string())), diff --git a/kclvm/tools/src/LSP/src/hover.rs b/kclvm/tools/src/LSP/src/hover.rs index c2d90c670..a54ea2bac 100644 --- a/kclvm/tools/src/LSP/src/hover.rs +++ b/kclvm/tools/src/LSP/src/hover.rs @@ -5,7 +5,7 @@ use kclvm_sema::{ core::global_state::GlobalState, ty::{FunctionType, ANY_TYPE_STR}, }; -use lsp_types::{Hover, HoverContents, MarkedString}; +use lsp_types::{Hover, HoverContents, LanguageString, MarkedString}; use crate::goto_def::find_def_with_gs; @@ -17,6 +17,7 @@ pub(crate) fn hover( gs: &GlobalState, ) -> Option { let mut docs: Vec = vec![]; + let mut pkg_path = String::new(); let def = find_def_with_gs(kcl_pos, gs, true); match def { Some(def_ref) => match gs.get_symbols().get_symbol(def_ref) { @@ -27,25 +28,22 @@ pub(crate) fn hover( // Schema Definition hover // ``` // pkg - // schema Foo(Base)[param: type] + // schema Foo(Base)[param: type]: // ----------------- - // doc - // ----------------- - // Attributes: // attr1: type // attr2? type + // ----------------- + // doc // ``` let schema_ty = ty.into_schema_type(); - docs.push(schema_ty.schema_ty_signature_str()); - if !schema_ty.doc.is_empty() { - docs.push(schema_ty.doc.clone()); - } + let (pkgpath, rest_sign) = schema_ty.schema_ty_signature_str(); + pkg_path = pkgpath; // The attr of schema_ty does not contain the attrs from inherited base schema. // Use the api provided by GlobalState to get all attrs let module_info = gs.get_packages().get_module_info(&kcl_pos.filename); let schema_attrs = obj.get_all_attributes(gs.get_symbols(), module_info); - let mut attrs = vec!["Attributes:".to_string()]; + let mut attrs: Vec = vec![]; for schema_attr in schema_attrs { if let kclvm_sema::core::symbol::SymbolKind::Attribute = schema_attr.get_kind() @@ -59,14 +57,24 @@ pub(crate) fn hover( None => ANY_TYPE_STR.to_string(), }; attrs.push(format!( - "{}{}: {}", + "\t{}{}: {}", name, if attr_symbol.is_optional() { "?" } else { "" }, attr_ty_str, )); } } - docs.push(attrs.join("\n\n")); + + let merged_doc = if !attrs.is_empty() { + format!("{}\n{}", rest_sign.clone(), attrs.join("\n")) + } else { + rest_sign.clone() + }; + docs.push(merged_doc); + + if !schema_ty.doc.is_empty() { + docs.push(schema_ty.doc.clone()); + } } _ => {} }, @@ -120,24 +128,33 @@ pub(crate) fn hover( }, None => {} } - docs_to_hover(docs) + docs_to_hover(docs, pkg_path) } // Convert docs to Hover. This function will convert to // None, Scalar or Array according to the number of positions -fn docs_to_hover(docs: Vec) -> Option { - match docs.len() { +fn docs_to_hover(docs: Vec, pkg_path: String) -> Option { + let mut all_docs = Vec::new(); + + if !pkg_path.is_empty() { + all_docs.push(MarkedString::String(pkg_path)); + } + + all_docs.extend(docs.iter().map(|doc| { + MarkedString::LanguageString(LanguageString { + language: "KCL".to_owned(), + value: doc.clone(), + }) + })); + + match all_docs.len() { 0 => None, 1 => Some(Hover { - contents: HoverContents::Scalar(MarkedString::String(docs[0].clone())), + contents: HoverContents::Scalar(all_docs.remove(0)), range: None, }), _ => Some(Hover { - contents: HoverContents::Array( - docs.iter() - .map(|doc| MarkedString::String(doc.clone())) - .collect(), - ), + contents: HoverContents::Array(all_docs), range: None, }), } @@ -186,7 +203,7 @@ mod tests { use std::path::PathBuf; use kclvm_error::Position as KCLPos; - use lsp_types::MarkedString; + use lsp_types::{LanguageString, MarkedString}; use proc_macro_crate::bench_test; use crate::tests::compile_test_file; @@ -213,13 +230,13 @@ mod tests { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "pkg\n\nschema Person"); + assert_eq!(s, "pkg"); } if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "hover doc test"); + assert_eq!(s, "schema Person:"); } if let MarkedString::String(s) = vec[2].clone() { - assert_eq!(s, "Attributes:\n\nname: str\n\nage: int"); + assert_eq!(s, "\tname: str\n\tage: int"); } } _ => unreachable!("test error"), @@ -251,9 +268,9 @@ mod tests { ]; // When converting to hover content - let hover = docs_to_hover(docs.clone()); + let hover = docs_to_hover(docs.clone(), "".to_string()); - // Then the result should be a Hover object with an Array of MarkedString::String + // Then the result should be a Hover object with an Array of MarkedString::LanguageString assert!(hover.is_some()); let hover = hover.unwrap(); match hover.contents { @@ -261,15 +278,24 @@ mod tests { assert_eq!(vec.len(), 3); assert_eq!( vec[0], - MarkedString::String("Documentation string 1".to_string()) + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "Documentation string 1".to_string() + }) ); assert_eq!( vec[1], - MarkedString::String("Documentation string 2".to_string()) + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "Documentation string 2".to_string() + }) ); assert_eq!( vec[2], - MarkedString::String("Documentation string 3".to_string()) + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "Documentation string 3".to_string() + }) ); } _ => panic!("Unexpected hover contents"), @@ -291,13 +317,13 @@ mod tests { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "__main__\n\nschema Person"); + assert_eq!(s, "__main__"); } if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "hover doc test"); + assert_eq!(s, "schema Person:\n\tname: str\n\tage?: int"); } if let MarkedString::String(s) = vec[2].clone() { - assert_eq!(s, "Attributes:\n\nname: str\n\nage?: int"); + assert_eq!(s, "hover doc test"); } } _ => unreachable!("test error"), @@ -319,10 +345,7 @@ mod tests { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "name: str"); - } - if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "name doc test"); + assert_eq!(s, "name: str\nname doc test"); } } _ => unreachable!("test error"), @@ -338,10 +361,7 @@ mod tests { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "age: int"); - } - if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "age doc test"); + assert_eq!(s, "age: int\nage doc test"); } } _ => unreachable!("test error"), @@ -363,10 +383,7 @@ mod tests { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "fn f(x: any) -> any"); - } - if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "lambda documents"); + assert_eq!(s, "fn f(x: any) -> any\nlambda documents"); } } _ => unreachable!("test error"), @@ -526,10 +543,10 @@ mod tests { lsp_types::HoverContents::Array(vec) => { assert_eq!(vec.len(), 2); if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "fib\n\nschema Fib"); + assert_eq!(s, "fib"); } if let MarkedString::String(s) = vec[1].clone() { - assert_eq!(s, "Attributes:\n\nn: int\n\nvalue: int"); + assert_eq!(s, "schema Fib:\n\tn: int\n\tvalue: int"); } } _ => unreachable!("test error"), @@ -586,11 +603,16 @@ mod tests { column: Some(1), }; let got = hover(&program, &pos, &gs).unwrap(); - let expect_content = vec![MarkedString::String( - "fn deprecated(version: str, reason: str, strict: bool) -> any".to_string(), - ), MarkedString::String( - "This decorator is used to get the deprecation message according to the wrapped key-value pair.".to_string(), - )]; + let expect_content = vec![ + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "fn deprecated(version: str, reason: str, strict: bool) -> any".to_string(), + }), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "This decorator is used to get the deprecation message according to the wrapped key-value pair.".to_string(), + }), + ]; match got.contents { lsp_types::HoverContents::Array(vec) => { assert_eq!(vec, expect_content) @@ -624,8 +646,11 @@ mod tests { let got = hover(&program, &pos, &gs).unwrap(); let expect_content = vec![ - MarkedString::String("__main__\n\nschema Data1\\[m: {str:str}](Data)".to_string()), - MarkedString::String("Attributes:\n\nname: str\n\nage: int".to_string()), + MarkedString::String("__main__".to_string()), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "schema Data1[m: {str:str}](Data):\n\tname: str\n\tage: int".to_string(), + }), ]; match got.contents { @@ -647,7 +672,15 @@ mod tests { column: Some(5), }; let got = hover(&program, &pos, &gs).unwrap(); - insta::assert_snapshot!(format!("{:?}", got)); + + match got.contents { + lsp_types::HoverContents::Scalar(marked_string) => { + if let MarkedString::String(s) = marked_string { + assert_eq!(s, "name: int"); + } + } + _ => unreachable!("test error"), + } let pos = KCLPos { filename: file.clone(), @@ -655,7 +688,12 @@ mod tests { column: Some(5), }; let got = hover(&program, &pos, &gs).unwrap(); - insta::assert_snapshot!(format!("{:?}", got)); + let expected = + lsp_types::HoverContents::Scalar(MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "name: int".to_string(), + })); + assert_eq!(got.contents, expected); let pos = KCLPos { filename: file.clone(), @@ -663,6 +701,11 @@ mod tests { column: Some(5), }; let got = hover(&program, &pos, &gs).unwrap(); - insta::assert_snapshot!(format!("{:?}", got)); + let expected = + lsp_types::HoverContents::Scalar(MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "name: int".to_string(), + })); + assert_eq!(got.contents, expected); } } diff --git a/kclvm/tools/src/LSP/src/tests.rs b/kclvm/tools/src/LSP/src/tests.rs index 8e750d8bb..dcc4c031f 100644 --- a/kclvm/tools/src/LSP/src/tests.rs +++ b/kclvm/tools/src/LSP/src/tests.rs @@ -25,6 +25,7 @@ use lsp_types::Hover; use lsp_types::HoverContents; use lsp_types::HoverParams; use lsp_types::InitializeParams; +use lsp_types::LanguageString; use lsp_types::MarkedString; use lsp_types::PublishDiagnosticsParams; use lsp_types::ReferenceContext; @@ -1169,9 +1170,15 @@ fn hover_test() { res.result.unwrap(), to_json(Hover { contents: HoverContents::Array(vec![ - MarkedString::String("__main__\n\nschema Person".to_string()), - MarkedString::String("hover doc test".to_string()), - MarkedString::String("Attributes:\n\nname: str\n\nage?: int".to_string()), + MarkedString::String("__main__".to_string()), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "schema Person:\n\tname: str\n\tage?: int".to_string() + }), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "hover doc test".to_string() + }), ]), range: None }) @@ -1225,7 +1232,10 @@ fn hover_assign_in_lambda_test() { assert_eq!( res.result.unwrap(), to_json(Hover { - contents: HoverContents::Scalar(MarkedString::String("images: [str]".to_string()),), + contents: HoverContents::Scalar(MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "images: [str]".to_string(), + })), range: None }) .unwrap() @@ -1663,12 +1673,17 @@ fn konfig_hover_test_main() { let got = hover(&program, &pos, &gs).unwrap(); match got.contents { HoverContents::Array(arr) => { - let expect: Vec = ["base.pkg.kusion_models.kube.frontend\n\nschema Server", - "Server is abstaction of Deployment and StatefulSet.", - "Attributes:\n\nname?: str\n\nworkloadType: str(Deployment) | str(StatefulSet)\n\nrenderType?: str(Server) | str(KubeVelaApplication)\n\nreplicas: int\n\nimage: str\n\nschedulingStrategy: SchedulingStrategy\n\nmainContainer: Main\n\nsidecarContainers?: [Sidecar]\n\ninitContainers?: [Sidecar]\n\nuseBuiltInLabels?: bool\n\nlabels?: {str:str}\n\nannotations?: {str:str}\n\nuseBuiltInSelector?: bool\n\nselector?: {str:str}\n\npodMetadata?: ObjectMeta\n\nvolumes?: [Volume]\n\nneedNamespace?: bool\n\nenableMonitoring?: bool\n\nconfigMaps?: [ConfigMap]\n\nsecrets?: [Secret]\n\nservices?: [Service]\n\ningresses?: [Ingress]\n\nserviceAccount?: ServiceAccount\n\nstorage?: ObjectStorage\n\ndatabase?: DataBase"] - .iter() - .map(|s| MarkedString::String(s.to_string())) - .collect(); + let expect: Vec = vec![ + MarkedString::String("base.pkg.kusion_models.kube.frontend".to_string()), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "schema Server:\n\tname?: str\n\tworkloadType: str(Deployment) | str(StatefulSet)\n\trenderType?: str(Server) | str(KubeVelaApplication)\n\treplicas: int\n\timage: str\n\tschedulingStrategy: SchedulingStrategy\n\tmainContainer: Main\n\tsidecarContainers?: [Sidecar]\n\tinitContainers?: [Sidecar]\n\tuseBuiltInLabels?: bool\n\tlabels?: {str:str}\n\tannotations?: {str:str}\n\tuseBuiltInSelector?: bool\n\tselector?: {str:str}\n\tpodMetadata?: ObjectMeta\n\tvolumes?: [Volume]\n\tneedNamespace?: bool\n\tenableMonitoring?: bool\n\tconfigMaps?: [ConfigMap]\n\tsecrets?: [Secret]\n\tservices?: [Service]\n\tingresses?: [Ingress]\n\tserviceAccount?: ServiceAccount\n\tstorage?: ObjectStorage\n\tdatabase?: DataBase".to_string(), + }), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "Server is abstaction of Deployment and StatefulSet.".to_string(), + }), + ]; assert_eq!(expect, arr); } _ => unreachable!("test error"), @@ -1683,13 +1698,16 @@ fn konfig_hover_test_main() { let got = hover(&program, &pos, &gs).unwrap(); match got.contents { HoverContents::Array(arr) => { - let expect: Vec = [ - "schedulingStrategy: SchedulingStrategy", - "SchedulingStrategy represents scheduling strategy.", - ] - .iter() - .map(|s| MarkedString::String(s.to_string())) - .collect(); + let expect: Vec = vec![ + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "schedulingStrategy: SchedulingStrategy".to_string(), + }), + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "SchedulingStrategy represents scheduling strategy.".to_string(), + }), + ]; assert_eq!(expect, arr); } _ => unreachable!("test error"), @@ -1706,7 +1724,10 @@ fn konfig_hover_test_main() { HoverContents::Scalar(s) => { assert_eq!( s, - MarkedString::String("appConfiguration: Server".to_string()) + MarkedString::LanguageString(LanguageString { + language: "KCL".to_string(), + value: "appConfiguration: Server".to_string(), + }) ); } _ => unreachable!("test error"),