diff --git a/kclvm/sema/src/resolver/doc.rs b/kclvm/sema/src/resolver/doc.rs index c73deb2f9..9586b8406 100644 --- a/kclvm/sema/src/resolver/doc.rs +++ b/kclvm/sema/src/resolver/doc.rs @@ -274,7 +274,7 @@ pub(crate) fn parse_doc_string(ori: &String) -> Doc { } /// The Doc struct contains a summary of schema and all the attributes described in the the docstring. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub(crate) struct Doc { pub summary: String, pub attrs: Vec, @@ -287,7 +287,7 @@ impl Doc { } /// The Attribute struct contains the attribute name and the corresponding description. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub(crate) struct Attribute { pub name: String, pub desc: Vec, diff --git a/kclvm/tools/src/LSP/src/hover.rs b/kclvm/tools/src/LSP/src/hover.rs index a0c62db72..7b8ad3d85 100644 --- a/kclvm/tools/src/LSP/src/hover.rs +++ b/kclvm/tools/src/LSP/src/hover.rs @@ -20,14 +20,51 @@ pub(crate) fn hover( if let crate::goto_def::Definition::Object(obj) = def { match obj.kind { ScopeObjectKind::Definition => { - docs.insert(obj.ty.ty_str()); - let doc = obj.ty.into_schema_type().doc.clone(); - if !doc.is_empty() { - docs.insert(doc); + // Schema Definition hover + // ``` + // pkg + // schema Foo(Base) + // ----------------- + // doc + // ----------------- + // Attributes: + // attr1: type + // attr2? type + // ``` + let schema_ty = obj.ty.into_schema_type(); + let base: String = if let Some(base) = schema_ty.base { + format!("({})", base.name) + } else { + "".to_string() + }; + docs.insert(format!( + "{}\n\nschema {}{}", + schema_ty.pkgpath, schema_ty.name, base + )); + if !schema_ty.doc.is_empty() { + docs.insert(schema_ty.doc.clone()); + } + let mut attrs = vec!["Attributes:".to_string()]; + for (name, attr) in schema_ty.attrs { + attrs.push(format!( + "{}{}:{}", + name, + if attr.is_optional { "?" } else { "" }, + format!(" {}", attr.ty.ty_str()), + )); } + docs.insert(attrs.join("\n\n")); } + // todo: hover ScopeObjectKind::Attribute optional, default value _ => { - docs.insert(obj.ty.ty_str()); + // Variable + // ``` + // name: type + //``` + docs.insert(format!("{}: {}", obj.name, obj.ty.ty_str())); + if let Some(doc) = obj.doc { + docs.insert(doc); + } } } } diff --git a/kclvm/tools/src/LSP/src/test_data/hover_test/hover.k b/kclvm/tools/src/LSP/src/test_data/hover_test/hover.k new file mode 100644 index 000000000..6f6976278 --- /dev/null +++ b/kclvm/tools/src/LSP/src/test_data/hover_test/hover.k @@ -0,0 +1,18 @@ +schema Person: + """ + hover doc test + + Attributes + ---------- + name : str, default is False, required + name doc test + age : int, default is False, optional + age doc test + """ + name: str + age?: int + + p = Person{ + name: "Alice" + age: 1 + } \ No newline at end of file diff --git a/kclvm/tools/src/LSP/src/tests.rs b/kclvm/tools/src/LSP/src/tests.rs index e34ee20f0..0021c856e 100644 --- a/kclvm/tools/src/LSP/src/tests.rs +++ b/kclvm/tools/src/LSP/src/tests.rs @@ -796,11 +796,17 @@ fn schema_doc_hover_test() { match got.contents { lsp_types::HoverContents::Array(vec) => { if let MarkedString::String(s) = vec[0].clone() { - assert_eq!(s, "Person"); + assert_eq!(s, "pkg\n\nschema Person"); } if let MarkedString::String(s) = vec[1].clone() { assert_eq!(s, "hover doc test"); } + if let MarkedString::String(s) = vec[2].clone() { + assert_eq!( + s, + "Attributes:\n\n__settings__?: {str:any}\n\nname: str\n\nage: int" + ); + } } _ => unreachable!("test error"), } @@ -813,7 +819,80 @@ fn schema_doc_hover_test() { match got.contents { lsp_types::HoverContents::Scalar(marked_string) => { if let MarkedString::String(s) = marked_string { - assert_eq!(s, "str"); + assert_eq!(s, "name: str"); + } + } + _ => unreachable!("test error"), + } +} + +#[test] +fn schema_doc_hover_test1() { + let (file, program, prog_scope, _) = compile_test_file("src/test_data/hover_test/hover.k"); + + let pos = KCLPos { + filename: file.clone(), + line: 15, + column: Some(11), + }; + let got = hover(&program, &pos, &prog_scope).unwrap(); + + match got.contents { + lsp_types::HoverContents::Array(vec) => { + if let MarkedString::String(s) = vec[0].clone() { + assert_eq!(s, "__main__\n\nschema Person"); + } + if let MarkedString::String(s) = vec[1].clone() { + assert_eq!(s, "hover doc test"); + } + if let MarkedString::String(s) = vec[2].clone() { + assert_eq!( + s, + "Attributes:\n\n__settings__?: {str:any}\n\nname: str\n\nage?: int" + ); + } + } + _ => unreachable!("test error"), + } +} + +#[test] +fn schema_attr_hover_test() { + let (file, program, prog_scope, _) = compile_test_file("src/test_data/hover_test/hover.k"); + + let pos = KCLPos { + filename: file.clone(), + line: 16, + column: Some(11), + }; + let got = hover(&program, &pos, &prog_scope).unwrap(); + + 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"); + } + } + _ => unreachable!("test error"), + } + + let pos = KCLPos { + filename: file.clone(), + line: 17, + column: Some(11), + }; + let got = hover(&program, &pos, &prog_scope).unwrap(); + + 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"); } } _ => unreachable!("test error"),