From 5fa208570b12524b26decfaa904326f7fc2724b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sm=C3=B3=C5=82ka?= Date: Tue, 12 Nov 2024 11:09:14 +0100 Subject: [PATCH] Added hover for path segments --- .../src/ide/hover/render/definition.rs | 11 + .../src/lang/inspect/defs.rs | 60 ++- .../tests/e2e/hover.rs | 3 +- .../tests/test_data/hover/basic.txt | 18 +- .../tests/test_data/hover/paths.txt | 373 ++++++++++++++++++ 5 files changed, 458 insertions(+), 7 deletions(-) create mode 100644 crates/cairo-lang-language-server/tests/test_data/hover/paths.txt diff --git a/crates/cairo-lang-language-server/src/ide/hover/render/definition.rs b/crates/cairo-lang-language-server/src/ide/hover/render/definition.rs index 6c2fb4b925d..9163dd5b8a1 100644 --- a/crates/cairo-lang-language-server/src/ide/hover/render/definition.rs +++ b/crates/cairo-lang-language-server/src/ide/hover/render/definition.rs @@ -32,6 +32,17 @@ pub fn definition( md } + SymbolDef::Module(module) => { + let mut md = String::new(); + md += &fenced_code_block(&module.definition_path()); + md += &fenced_code_block(&module.signature()); + if let Some(doc) = module.documentation(db) { + md += RULE; + md += &doc; + } + md + } + SymbolDef::Variable(var) => fenced_code_block(&var.signature(db)), SymbolDef::ExprInlineMacro(macro_name) => { let mut md = fenced_code_block(macro_name); diff --git a/crates/cairo-lang-language-server/src/lang/inspect/defs.rs b/crates/cairo-lang-language-server/src/lang/inspect/defs.rs index 7d0dc3e4bee..f70d81bafce 100644 --- a/crates/cairo-lang-language-server/src/lang/inspect/defs.rs +++ b/crates/cairo-lang-language-server/src/lang/inspect/defs.rs @@ -7,6 +7,7 @@ use cairo_lang_defs::ids::{ }; use cairo_lang_diagnostics::ToOption; use cairo_lang_doc::db::DocGroup; +use cairo_lang_doc::documentable_item::DocumentableItemId; use cairo_lang_parser::db::ParserGroup; use cairo_lang_semantic::db::SemanticGroup; use cairo_lang_semantic::expr::pattern::QueryPatternVariablesFromDb; @@ -38,6 +39,7 @@ pub enum SymbolDef { Variable(VariableDef), ExprInlineMacro(String), Member(MemberDef), + Module(ModuleDef), } /// Information about a struct member. @@ -78,7 +80,6 @@ impl SymbolDef { match definition_item { ResolvedItem::Generic(ResolvedGenericItem::GenericConstant(_)) - | ResolvedItem::Generic(ResolvedGenericItem::Module(_)) | ResolvedItem::Generic(ResolvedGenericItem::GenericFunction(_)) | ResolvedItem::Generic(ResolvedGenericItem::TraitFunction(_)) | ResolvedItem::Generic(ResolvedGenericItem::GenericType(_)) @@ -88,7 +89,6 @@ impl SymbolDef { | ResolvedItem::Generic(ResolvedGenericItem::Trait(_)) | ResolvedItem::Generic(ResolvedGenericItem::Impl(_)) | ResolvedItem::Concrete(ResolvedConcreteItem::Constant(_)) - | ResolvedItem::Concrete(ResolvedConcreteItem::Module(_)) | ResolvedItem::Concrete(ResolvedConcreteItem::Function(_)) | ResolvedItem::Concrete(ResolvedConcreteItem::TraitFunction(_)) | ResolvedItem::Concrete(ResolvedConcreteItem::Type(_)) @@ -98,6 +98,14 @@ impl SymbolDef { ItemDef::new(db, &definition_node).map(Self::Item) } + ResolvedItem::Generic(ResolvedGenericItem::Module(id)) => { + Some(Self::Module(ModuleDef::new(db, id))) + } + + ResolvedItem::Concrete(ResolvedConcreteItem::Module(id)) => { + Some(Self::Module(ModuleDef::new(db, id))) + } + ResolvedItem::Generic(ResolvedGenericItem::Variable(_)) => { VariableDef::new(db, definition_node).map(Self::Variable) } @@ -290,6 +298,54 @@ impl VariableDef { } } +/// Information about the definition of a module. +pub struct ModuleDef { + id: ModuleId, + name: SmolStr, + /// A full path to the parent module if [`ModuleId`] points to a submodule, + /// None otherwise (i.e. for a crate root). + parent_full_path: Option, +} + +impl ModuleDef { + /// Constructs new [`ModuleDef`] instance. + pub fn new(db: &AnalysisDatabase, id: ModuleId) -> Self { + let name = id.name(db); + let parent_full_path = id + .full_path(db) + .strip_suffix(name.as_str()) + .unwrap() + .strip_suffix("::") // Fails when path lacks `::`, i.e. when we import from a crate root. + .map(String::from); + + ModuleDef { id, name, parent_full_path } + } + + /// Gets the module signature: a name preceded by a qualifier: "mod" for submodule + /// and "crate" for crate root. + pub fn signature(&self) -> String { + let prefix = if self.parent_full_path.is_some() { "mod" } else { "crate" }; + format!("{prefix} {}", self.name) + } + + /// Gets the full path of a parent module of the current module. + pub fn definition_path(&self) -> String { + self.parent_full_path.clone().unwrap_or_default() + } + + /// Gets the module's documentation if it's available. + pub fn documentation(&self, db: &AnalysisDatabase) -> Option { + let doc_id = match self.id { + ModuleId::CrateRoot(id) => DocumentableItemId::Crate(id), + ModuleId::Submodule(id) => DocumentableItemId::LookupItem(LookupItemId::ModuleItem( + ModuleItemId::Submodule(id), + )), + }; + + db.get_item_documentation(doc_id) + } +} + // TODO(mkaput): make private. pub fn find_definition( db: &AnalysisDatabase, diff --git a/crates/cairo-lang-language-server/tests/e2e/hover.rs b/crates/cairo-lang-language-server/tests/e2e/hover.rs index 2f61166c359..a036c13e032 100644 --- a/crates/cairo-lang-language-server/tests/e2e/hover.rs +++ b/crates/cairo-lang-language-server/tests/e2e/hover.rs @@ -17,7 +17,8 @@ cairo_lang_test_utils::test_file_test!( partial: "partial.txt", starknet: "starknet.txt", literals: "literals.txt", - structs: "structs.txt" + structs: "structs.txt", + paths: "paths.txt", }, test_hover ); diff --git a/crates/cairo-lang-language-server/tests/test_data/hover/basic.txt b/crates/cairo-lang-language-server/tests/test_data/hover/basic.txt index 9e04d2aada4..a3106c47574 100644 --- a/crates/cairo-lang-language-server/tests/test_data/hover/basic.txt +++ b/crates/cairo-lang-language-server/tests/test_data/hover/basic.txt @@ -170,21 +170,31 @@ fn add_two(x: u32) -> u32 // = source context front_of_house::hosting::add_to_waitlist(); // = highlight -No highlight information. + front_of_house::hosting::add_to_waitlist(); // = popover ```cairo -fn add_to_waitlist() -> () +hello ``` +```cairo +mod front_of_house +``` +--- +Front of house module. //! > hover #7 // = source context front_of_house::hosting::add_to_waitlist(); // = highlight -No highlight information. + front_of_house::hosting::add_to_waitlist(); // = popover ```cairo -fn add_to_waitlist() -> () +hello::front_of_house +``` +```cairo +mod hosting ``` +--- +Hosting module. //! > hover #8 // = source context diff --git a/crates/cairo-lang-language-server/tests/test_data/hover/paths.txt b/crates/cairo-lang-language-server/tests/test_data/hover/paths.txt new file mode 100644 index 00000000000..994677ba823 --- /dev/null +++ b/crates/cairo-lang-language-server/tests/test_data/hover/paths.txt @@ -0,0 +1,373 @@ +//! > Hover + +//! > test_runner_name +test_hover + +//! > cairo_project.toml +[crate_roots] +hello = "src" + +[config.global] +edition = "2023_11" + +//! > cairo_code +/// some_module docstring. +mod some_module { + /// internal_module docstring. + pub mod internal_module { + pub trait MyTrait { + fn foo(t: T); + } + + pub impl MyTraitImpl of MyTrait { + fn foo(t: u32) {} + } + + pub mod nested_internal_module { + pub struct PublicStruct {} + struct PrivateStruct {} + + pub fn foo() {} + } + } + + /// internal_module2 docstring. + pub mod internal_module2 { + use super::internal_module; + + /// private_submodule docstring. + mod private_submodule { + struct PrivateStruct2 {} + } + } +} + +mod happy_cases { + use super::some_module; + use super::some_module::internal_module; + use super::some_module::internal_module::MyTrait; + use super::some_module::internal_module::nested_internal_module; + + impl TraitImpl of super::some_module::internal_module::MyTrait { + fn foo(t: felt252) {} + } + + impl TraitImpl2 of internal_module::MyTrait { + fn foo(t: u8) {} + } + + fn function_with_path() { + super::some_module::internal_module::nested_internal_module::foo(); + super::some_module::internal_module::nested_internal_module::foo(); + super::some_module::internal_module::nested_internal_module::foo(); + super::some_module::internal_module::nested_internal_module::foo(); + } + + fn function_with_partial_path() { + nested_internal_module::foo(); + internal_module::MyTraitImpl::foo(0_u32); + } + + fn constructor_with_path() { + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; + } +} + +// Although the code itself is semantically invalid because of items' visibility, paths should be shown correctly. +mod unhappy_cases { + use super::some_module::internal_module; + use super::some_module::internal_module2::private_submodule; + + fn private_item { + let _ = super::some_module::internal_module::PrivateStruct {}; + let _ = private_submodule::PrivateStruct2 {}; + } +} + +//! > hover #0 +// = source context +mod some_module { +// = highlight +mod some_module { +// = popover +```cairo +hello +``` +```cairo +mod some_module +``` +--- +some_module docstring. + +//! > hover #1 +// = source context + pub mod internal_module { +// = highlight + pub mod internal_module { +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #2 +// = source context + use super::internal_module; +// = highlight + use super::internal_module; +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #3 +// = source context + mod private_submodule { +// = highlight + mod private_submodule { +// = popover +```cairo +hello::some_module::internal_module2 +``` +```cairo +mod private_submodule +``` +--- +private_submodule docstring. + +//! > hover #4 +// = source context + use super::some_module; +// = highlight + use super::some_module; +// = popover +```cairo +hello +``` +```cairo +mod some_module +``` +--- +some_module docstring. + +//! > hover #5 +// = source context + use super::some_module::internal_module::MyTrait; +// = highlight + use super::some_module::internal_module::MyTrait; +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #6 +// = source context + use super::some_module::internal_module::nested_internal_module; +// = highlight + use super::some_module::internal_module::nested_internal_module; +// = popover +```cairo +hello::some_module::internal_module +``` +```cairo +mod nested_internal_module +``` + +//! > hover #7 +// = source context + impl TraitImpl of super::some_module::internal_module::MyTrait { +// = highlight + impl TraitImpl of super::some_module::internal_module::MyTrait { +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #8 +// = source context + impl TraitImpl2 of internal_module::MyTrait { +// = highlight + impl TraitImpl2 of internal_module::MyTrait { +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #9 +// = source context + super::some_module::internal_module::nested_internal_module::foo(); +// = highlight + super::some_module::internal_module::nested_internal_module::foo(); +// = popover +```cairo +hello::happy_cases +``` +```cairo +fn function_with_path() +``` + +//! > hover #10 +// = source context + super::some_module::internal_module::nested_internal_module::foo(); +// = highlight + super::some_module::internal_module::nested_internal_module::foo(); +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #11 +// = source context + super::some_module::internal_module::nested_internal_module::foo(); +// = highlight + super::some_module::internal_module::nested_internal_module::foo(); +// = popover +```cairo +hello::some_module::internal_module +``` +```cairo +mod nested_internal_module +``` + +//! > hover #12 +// = source context + nested_internal_module::foo(); +// = highlight + nested_internal_module::foo(); +// = popover +```cairo +hello::some_module::internal_module +``` +```cairo +mod nested_internal_module +``` + +//! > hover #13 +// = source context + internal_module::MyTraitImpl::foo(0_u32); +// = highlight + internal_module::MyTraitImpl::foo(0_u32); +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #14 +// = source context + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; +// = highlight +No highlight information. +// = popover +```cairo +hello::some_module::internal_module::nested_internal_module::PublicStruct +``` + +//! > hover #15 +// = source context + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; +// = highlight + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #16 +// = source context + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; +// = highlight + let _ = super::some_module::internal_module::nested_internal_module::PublicStruct {}; +// = popover +```cairo +hello::some_module::internal_module +``` +```cairo +mod nested_internal_module +``` + +//! > hover #17 +// = source context + use super::some_module::internal_module2::private_submodule; +// = highlight + use super::some_module::internal_module2::private_submodule; +// = popover +```cairo +hello::some_module::internal_module2 +``` +```cairo +mod private_submodule +``` +--- +private_submodule docstring. + +//! > hover #18 +// = source context + let _ = super::some_module::internal_module::PrivateStruct {}; +// = highlight + let _ = super::some_module::internal_module::PrivateStruct {}; +// = popover +```cairo +hello::some_module +``` +```cairo +mod internal_module +``` +--- +internal_module docstring. + +//! > hover #19 +// = source context + let _ = private_submodule::PrivateStruct2 {}; +// = highlight + let _ = private_submodule::PrivateStruct2 {}; +// = popover +```cairo +hello::some_module::internal_module2 +``` +```cairo +mod private_submodule +``` +--- +private_submodule docstring.