Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LS: Add hovers for path segments #6649

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
60 changes: 58 additions & 2 deletions crates/cairo-lang-language-server/src/lang/inspect/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,6 +39,7 @@ pub enum SymbolDef {
Variable(VariableDef),
ExprInlineMacro(String),
Member(MemberDef),
Module(ModuleDef),
}

/// Information about a struct member.
Expand Down Expand Up @@ -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(_))
Expand All @@ -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(_))
Expand All @@ -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)
}
Expand Down Expand Up @@ -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<String>,
}

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<String> {
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,
Expand Down
3 changes: 2 additions & 1 deletion crates/cairo-lang-language-server/tests/e2e/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down
18 changes: 14 additions & 4 deletions crates/cairo-lang-language-server/tests/test_data/hover/basic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,31 @@ fn add_two(x: u32) -> u32
// = source context
front<caret>_of_house::hosting::add_to_waitlist();
// = highlight
No highlight information.
<sel>front_of_house</sel>::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::ho<caret>sting::add_to_waitlist();
// = highlight
No highlight information.
front_of_house::<sel>hosting</sel>::add_to_waitlist();
// = popover
```cairo
fn add_to_waitlist() -> ()
hello::front_of_house
```
```cairo
mod hosting
```
---
Hosting module.

//! > hover #8
// = source context
Expand Down
Loading