use cairo_lang_filesystem::db::get_originating_location;
use cairo_lang_filesystem::ids::SpanInFile;
use cairo_lang_semantic::lsp_helpers::LspHelpers;
use cairo_lang_syntax::node::ast::{TerminalIdentifier, TerminalUnderscore};
use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
use cairo_language_common::CommonGroup;
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind};
use crate::ide::markdown::RULE;
use crate::lang::db::{AnalysisDatabase, LsSyntaxGroup};
use crate::lang::lsp::{LsProtoGroup, ToCairo, ToLsp};
mod render;
pub fn hover(params: HoverParams, db: &AnalysisDatabase) -> 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 node = db
.find_identifier_at_position(file_id, position)
.map(|ident| ident.as_syntax_node())
.or_else(|| db.find_syntax_node_at_position(file_id, position))?;
let resultants = db.get_node_resultants(node)?;
let content = resultants
.iter()
.filter_map(|node| render_hover(db, *node))
.collect::<OrderedHashSet<_>>() .into_iter()
.reduce(|value1, value2| format!("{value1}\n{RULE}{value2}"));
let (content, node) = match (content, node) {
(Some(content), node) => (content, node),
_ => resultants.iter().find_map(|node| render::type_info(db, *node))?,
};
let SpanInFile { file_id: range_file_id, span } = get_originating_location(
db,
SpanInFile { file_id: node.stable_ptr(db).file_id(db), span: node.span_without_trivia(db) },
None,
);
let range = (range_file_id == file_id)
.then(|| span.position_in_file(db, file_id).map(|p| p.to_lsp()))
.flatten();
Some(Hover {
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: content,
}),
range,
})
}
fn render_hover<'db>(db: &'db AnalysisDatabase, node: SyntaxNode<'db>) -> Option<String> {
let file_id = node.stable_ptr(db).file_id(db);
let importables = db.visible_importables_from_module(db.find_module_containing_node(node)?)?;
if let Some(hover) = render::literal(db, node, &importables) {
return Some(hover);
}
if let Some(hover) = node
.ancestor_of_type::<TerminalUnderscore>(db)
.and_then(|underscore| render::ty(db, underscore, &importables))
{
return Some(hover);
}
if let Some(hover) = node
.ancestors_with_self(db)
.find_map(|node| TerminalIdentifier::cast(db, node))
.and_then(|ref id| render::definition(db, id, file_id, &importables))
{
return Some(hover);
}
if node.kind(db).is_keyword_token() {
return render::keyword(db, node.kind(db));
}
None
}