-
Notifications
You must be signed in to change notification settings - Fork 556
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LS: Clean up hover definition logic (#5780)
- Loading branch information
Showing
12 changed files
with
311 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use cairo_lang_compiler::db::RootDatabase; | ||
use cairo_lang_utils::Upcast; | ||
use tower_lsp::lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; | ||
|
||
use crate::lang::lsp::{LsProtoGroup, ToCairo}; | ||
use crate::lang::syntax::LsSyntaxGroup; | ||
use crate::markdown::Markdown; | ||
|
||
mod render; | ||
|
||
/// Get hover information at a given text document position. | ||
#[tracing::instrument( | ||
level = "debug", | ||
skip_all, | ||
fields(uri = %params.text_document_position_params.text_document.uri) | ||
)] | ||
pub fn hover(params: HoverParams, db: &RootDatabase) -> Option<Hover> { | ||
let file_id = db.file_for_url(¶ms.text_document_position_params.text_document.uri)?; | ||
let position = params.text_document_position_params.position.to_cairo(); | ||
let identifier = db.find_identifier_at_position(file_id, position)?; | ||
|
||
render::definition(db.upcast(), &identifier, file_id) | ||
|
||
// TODO(mkaput): If client only supports plaintext, strip markdown formatting here like RA. | ||
} | ||
|
||
/// Convenience shortcut for building hover contents from markdown block. | ||
fn markdown_contents(md: Markdown) -> HoverContents { | ||
HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value: md.into() }) | ||
} |
56 changes: 56 additions & 0 deletions
56
crates/cairo-lang-language-server/src/ide/hover/render/definition.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use cairo_lang_compiler::db::RootDatabase; | ||
use cairo_lang_defs::db::DefsGroup; | ||
use cairo_lang_filesystem::ids::FileId; | ||
use cairo_lang_syntax::node::ast::TerminalIdentifier; | ||
use cairo_lang_syntax::node::TypedSyntaxNode; | ||
use cairo_lang_utils::Upcast; | ||
use tower_lsp::lsp_types::Hover; | ||
|
||
use crate::find_definition; | ||
use crate::ide::hover::markdown_contents; | ||
use crate::lang::lsp::ToLsp; | ||
use crate::lang::semantic::LsSemanticGroup; | ||
use crate::markdown::Markdown; | ||
|
||
/// Get declaration and documentation "definition" of an item referred by the given identifier. | ||
#[tracing::instrument(level = "trace", skip_all)] | ||
pub fn definition( | ||
db: &RootDatabase, | ||
identifier: &TerminalIdentifier, | ||
file_id: FileId, | ||
) -> Option<Hover> { | ||
// Get the syntax node of the definition. | ||
let definition_node = { | ||
let lookup_items = db.collect_lookup_items_stack(&identifier.as_syntax_node())?; | ||
let stable_ptr = find_definition(db, identifier, &lookup_items)?; | ||
stable_ptr.lookup(db.upcast()) | ||
}; | ||
// Get the lookup item representing the defining item. | ||
let lookup_item_id = db.find_lookup_item(&definition_node)?; | ||
|
||
let mut md = Markdown::empty(); | ||
|
||
let definition = db.get_item_definition(lookup_item_id); | ||
// TODO(mkaput): Format this with Cairo formatter. | ||
md += Markdown::fenced_code_block(&definition); | ||
|
||
let documentation = db.get_item_documentation(lookup_item_id).unwrap_or_default(); | ||
|
||
if !documentation.is_empty() { | ||
md += Markdown::rule(); | ||
|
||
let mut doc = Markdown::from(documentation); | ||
doc.convert_fenced_code_blocks_to_cairo(); | ||
doc.ensure_trailing_newline(); | ||
md += doc; | ||
} | ||
|
||
Some(Hover { | ||
contents: markdown_contents(md), | ||
range: identifier | ||
.as_syntax_node() | ||
.span_without_trivia(db.upcast()) | ||
.position_in_file(db.upcast(), file_id) | ||
.map(|p| p.to_lsp()), | ||
}) | ||
} |
3 changes: 3 additions & 0 deletions
3
crates/cairo-lang-language-server/src/ide/hover/render/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod definition; | ||
|
||
pub use self::definition::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,6 +103,7 @@ mod env_config; | |
mod ide; | ||
mod lang; | ||
mod lsp; | ||
mod markdown; | ||
mod project; | ||
mod server; | ||
mod toolchain; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use std::borrow::Cow; | ||
use std::fmt; | ||
use std::ops::AddAssign; | ||
|
||
use itertools::Itertools; | ||
|
||
#[cfg(test)] | ||
#[path = "markdown_test.rs"] | ||
mod test; | ||
|
||
/// A convenience wrapper for building Markdown texts for used to display rich text in the IDE. | ||
/// | ||
/// Markdown is used because this is the format used by the LSP protocol for rich text. | ||
#[derive(Default, Debug)] | ||
pub struct Markdown { | ||
text: String, | ||
} | ||
|
||
/// Constructors and primitive operations. | ||
impl Markdown { | ||
/// Creates a new [`Markdown`] instance with empty text. | ||
pub fn empty() -> Self { | ||
Default::default() | ||
} | ||
|
||
/// Horizontal rule. | ||
pub fn rule() -> Self { | ||
"\n---\n".into() | ||
} | ||
|
||
/// Creates a new [`Markdown`] instance with the given code surrounded with `cairo` fenced code | ||
/// block. | ||
pub fn fenced_code_block(contents: &str) -> Self { | ||
format!("```cairo\n{contents}\n```").into() | ||
} | ||
|
||
/// Appends the given Markdown text to the current text. | ||
pub fn append(&mut self, other: Self) { | ||
self.text.push_str(&other.text); | ||
} | ||
} | ||
|
||
impl From<Markdown> for String { | ||
fn from(markdown: Markdown) -> Self { | ||
markdown.text | ||
} | ||
} | ||
|
||
impl From<String> for Markdown { | ||
fn from(text: String) -> Self { | ||
Markdown { text } | ||
} | ||
} | ||
|
||
impl From<&str> for Markdown { | ||
fn from(text: &str) -> Self { | ||
text.to_owned().into() | ||
} | ||
} | ||
|
||
impl fmt::Display for Markdown { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt::Display::fmt(&self.text, f) | ||
} | ||
} | ||
|
||
impl AddAssign for Markdown { | ||
fn add_assign(&mut self, rhs: Self) { | ||
self.append(rhs) | ||
} | ||
} | ||
|
||
/// High-level operations used throughout LS functionality. | ||
impl Markdown { | ||
/// Adds `cairo` language code to all fenced code blocks in the text that do not specify | ||
/// language. | ||
pub fn convert_fenced_code_blocks_to_cairo(&mut self) { | ||
let mut in_cairo_fence = false; | ||
self.text = self | ||
.text | ||
.lines() | ||
.map(|line| match (line.strip_prefix("```"), in_cairo_fence) { | ||
// Start of a fenced code block without language code. | ||
(Some(rest), false) if rest.trim_start().is_empty() => { | ||
in_cairo_fence = true; | ||
Cow::Owned(format!("```cairo{rest}")) | ||
} | ||
// Start of a fenced code block but with some language code. | ||
(Some(_), false) => { | ||
in_cairo_fence = true; | ||
Cow::Borrowed(line) | ||
} | ||
// End of a fenced code block. | ||
(Some(_), true) => { | ||
in_cairo_fence = false; | ||
Cow::Borrowed(line) | ||
} | ||
// Unrelated line. | ||
(None, _) => Cow::Borrowed(line), | ||
}) | ||
.join("\n"); | ||
} | ||
|
||
/// Ensures that the text ends with `\n`. | ||
pub fn ensure_trailing_newline(&mut self) { | ||
if !self.text.ends_with('\n') { | ||
self.text.push('\n'); | ||
} | ||
} | ||
} |
Oops, something went wrong.