use cairo_lang_defs::ids::{ConstantLongId, FunctionWithBodyId};
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::items::function_with_body::SemanticExprLookup;
use cairo_lang_semantic::lookup_item::LookupItemEx;
use cairo_lang_syntax::node::ast::{
Expr, ItemConstant, TerminalLiteralNumber, TerminalShortString, TerminalString,
};
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
use cairo_lang_utils::{Intern, Upcast};
use indoc::formatdoc;
use lsp_types::Hover;
use crate::ide::hover::markdown_contents;
use crate::lang::db::{AnalysisDatabase, LsSemanticGroup, LsSyntaxGroup};
use crate::lang::lsp::ToLsp;
#[tracing::instrument(level = "trace", skip_all)]
pub fn literal(db: &AnalysisDatabase, node: &SyntaxNode, file_id: FileId) -> Option<Hover> {
match node.kind(db) {
SyntaxKind::TokenLiteralNumber => {
let parent = node.parent()?;
let literal = TerminalLiteralNumber::from_syntax_node(db, parent.clone());
let ty = find_type(db, parent)?;
number_hover(db, &literal, &ty, file_id)
}
SyntaxKind::TokenString => {
let parent = node.parent()?;
let literal = TerminalString::from_syntax_node(db, parent.clone());
let ty = find_type(db, parent)?;
string_hover(db, &literal, &ty, file_id)
}
SyntaxKind::TokenShortString => {
let parent = node.parent()?;
let literal = TerminalShortString::from_syntax_node(db, parent.clone());
let ty = find_type(db, parent)?;
short_string_hover(db, &literal, &ty, file_id)
}
_ => None,
}
}
fn find_type(db: &AnalysisDatabase, node: SyntaxNode) -> Option<String> {
if let Some(function_id) = db.find_lookup_item(&node)?.function_with_body() {
find_type_in_function_context(db, node.clone(), function_id)
} else {
find_type_in_const_declaration(db, node)
}
}
fn find_type_in_function_context(
db: &AnalysisDatabase,
node: SyntaxNode,
function_id: FunctionWithBodyId,
) -> Option<String> {
let expr = Expr::from_syntax_node(db, node);
let expr_id = db.lookup_expr_by_ptr(function_id, expr.stable_ptr()).ok()?;
Some(db.expr_semantic(function_id, expr_id).ty().format(db))
}
fn find_type_in_const_declaration(db: &AnalysisDatabase, node: SyntaxNode) -> Option<String> {
let module_file_id = db.find_module_file_containing_node(&node)?;
let const_node = db.first_ancestor_of_kind(node, SyntaxKind::ItemConstant)?;
let const_item = ItemConstant::from_syntax_node(db, const_node);
let const_item_id = ConstantLongId(module_file_id, const_item.stable_ptr()).intern(db);
Some(db.constant_const_type(const_item_id).ok()?.format(db))
}
fn number_hover(
db: &AnalysisDatabase,
literal: &TerminalLiteralNumber,
ty: &str,
file_id: FileId,
) -> Option<Hover> {
let value = literal.numeric_value(db)?;
let representation = formatdoc!(
"
```cairo
{ty}
```
---
value of literal: `{value} ({value:#x} | {value:#b})`
"
);
Some(Hover {
contents: markdown_contents(representation),
range: literal
.as_syntax_node()
.span_without_trivia(db.upcast())
.position_in_file(db.upcast(), file_id)
.map(|position| position.to_lsp()),
})
}
fn string_hover(
db: &AnalysisDatabase,
literal: &TerminalString,
ty: &str,
file_id: FileId,
) -> Option<Hover> {
let representation = formatdoc!(
r#"
```cairo
{ty}
```
"#
);
Some(Hover {
contents: markdown_contents(representation),
range: literal
.as_syntax_node()
.span_without_trivia(db.upcast())
.position_in_file(db.upcast(), file_id)
.map(|position| position.to_lsp()),
})
}
fn short_string_hover(
db: &AnalysisDatabase,
literal: &TerminalShortString,
ty: &str,
file_id: FileId,
) -> Option<Hover> {
let representation = match (literal.numeric_value(db), literal.string_value(db)) {
(None, _) => None,
(Some(numeric), None) => Some(formatdoc!(
"
```cairo
{ty}
```
---
value of literal: `{numeric:#x}`
"
)),
(Some(numeric), Some(string)) => Some(formatdoc!(
"
```cairo
{ty}
```
---
value of literal: `'{string}' ({numeric:#x})`
"
)),
}?;
Some(Hover {
contents: markdown_contents(representation),
range: literal
.as_syntax_node()
.span_without_trivia(db.upcast())
.position_in_file(db.upcast(), file_id)
.map(|position| position.to_lsp()),
})
}