Skip to content

Commit d27c5dc

Browse files
committed
LSP: add the function and callback argument name on the tooltips
1 parent 929e71e commit d27c5dc

File tree

2 files changed

+81
-25
lines changed

2 files changed

+81
-25
lines changed

tools/lsp/language/hover.rs

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
use super::token_info::TokenInfo;
55
use crate::common::DocumentCache;
6-
use i_slint_compiler::langtype::{ElementType, PropertyLookupResult, Type};
7-
use i_slint_compiler::parser::SyntaxToken;
6+
use i_slint_compiler::langtype::{ElementType, Type};
7+
use i_slint_compiler::object_tree::ElementRc;
8+
use i_slint_compiler::parser::{syntax_nodes, SyntaxNode, SyntaxToken};
89
use itertools::Itertools as _;
910
use lsp_types::{Hover, HoverContents, MarkupContent};
1011

@@ -34,41 +35,85 @@ pub fn get_tooltip(document_cache: &mut DocumentCache, token: SyntaxToken) -> Op
3435
from_slint_code(&format!("{} := {} {{ /*...*/ }}", e.id, e.base_type))
3536
}
3637
}
37-
TokenInfo::NamedReference(nr) => {
38-
let prop_info = nr.element().borrow().lookup_property(nr.name());
39-
from_prop_result(prop_info)?
40-
}
38+
TokenInfo::NamedReference(nr) => from_property_in_element(&nr.element(), nr.name())?,
4139
TokenInfo::EnumerationValue(v) => from_slint_code(&format!("{}.{}", v.enumeration.name, v)),
4240
TokenInfo::FileName(_) => return None,
4341
// Todo: this can happen when there is some syntax error
4442
TokenInfo::LocalProperty(_) | TokenInfo::LocalCallback(_) => return None,
45-
TokenInfo::IncompleteNamedReference(el, name) => {
46-
let prop_info = el.lookup_property(&name);
47-
from_prop_result(prop_info)?
48-
}
43+
TokenInfo::IncompleteNamedReference(el, name) => from_property_in_type(&el, &name)?,
4944
};
5045

5146
Some(Hover { contents: HoverContents::Markup(contents), range: None })
5247
}
5348

54-
fn from_prop_result(prop_info: PropertyLookupResult) -> Option<MarkupContent> {
49+
fn from_property_in_element(element: &ElementRc, name: &str) -> Option<MarkupContent> {
50+
if let Some(decl) = element.borrow().property_declarations.get(name) {
51+
return property_tooltip(
52+
&decl.property_type,
53+
decl.node.as_ref(),
54+
name,
55+
decl.pure.unwrap_or(false),
56+
);
57+
}
58+
from_property_in_type(&element.borrow().base_type, name)
59+
}
60+
61+
fn from_property_in_type(base: &ElementType, name: &str) -> Option<MarkupContent> {
62+
match base {
63+
ElementType::Component(c) => from_property_in_element(&c.root_element, name),
64+
ElementType::Builtin(b) => {
65+
let resolved_name = b.native_class.lookup_alias(name).unwrap_or(name);
66+
let info = b.properties.get(resolved_name)?;
67+
property_tooltip(&info.ty, None, name, false)
68+
}
69+
_ => None,
70+
}
71+
}
72+
73+
fn property_tooltip(
74+
ty: &Type,
75+
node: Option<&SyntaxNode>,
76+
name: &str,
77+
pure: bool,
78+
) -> Option<MarkupContent> {
79+
let pure = if pure { "pure " } else { "" };
5580
let ret_ty =
5681
|ty: &Type| if matches!(ty, Type::Void) { String::new() } else { format!(" -> {}", ty) };
57-
58-
let pure = if prop_info.declared_pure.is_some_and(|x| x) { "pure " } else { "" };
59-
if let Type::Callback(callback) = &prop_info.property_type {
82+
if let Type::Callback(callback) = ty {
6083
let ret = ret_ty(&callback.return_type);
61-
let args = callback.args.iter().map(|x| x.to_string()).join(", ");
62-
Some(from_slint_code(&format!("{pure}callback {}({args}){ret}", prop_info.resolved_name)))
63-
} else if let Type::Function(function) = &prop_info.property_type {
84+
let args =
85+
if let Some(node) = node.cloned().and_then(syntax_nodes::CallbackDeclaration::new) {
86+
callback
87+
.args
88+
.iter()
89+
.zip(node.CallbackDeclarationParameter())
90+
.map(|(ty, n)| {
91+
if let Some(id) = n.DeclaredIdentifier() {
92+
format!("{}: {ty}", id.text())
93+
} else {
94+
ty.to_string()
95+
}
96+
})
97+
.join(", ")
98+
} else {
99+
callback.args.iter().map(|x| x.to_string()).join(", ")
100+
};
101+
Some(from_slint_code(&format!("{pure}callback {name}({args}){ret}")))
102+
} else if let Type::Function(function) = &ty {
64103
let ret = ret_ty(&function.return_type);
65-
let args = function.args.iter().map(|x| x.to_string()).join(", ");
66-
Some(from_slint_code(&format!("{pure}function {}({args}){ret}", prop_info.resolved_name)))
67-
} else if prop_info.property_type.is_property_type() {
68-
Some(from_slint_code(&format!(
69-
"property <{}> {}",
70-
prop_info.property_type, prop_info.resolved_name
71-
)))
104+
let args = if let Some(node) = node.cloned().and_then(syntax_nodes::Function::new) {
105+
function
106+
.args
107+
.iter()
108+
.zip(node.ArgumentDeclaration())
109+
.map(|(ty, n)| format!("{}: {ty}", n.DeclaredIdentifier().text()))
110+
.join(", ")
111+
} else {
112+
function.args.iter().map(|x| x.to_string()).join(", ")
113+
};
114+
Some(from_slint_code(&format!("{pure}function {name}({args}){ret}")))
115+
} else if ty.is_property_type() {
116+
Some(from_slint_code(&format!("property <{ty}> {name}")))
72117
} else {
73118
None
74119
}
@@ -94,6 +139,7 @@ mod tests {
94139
#[test]
95140
fn test_tooltip() {
96141
let source = r#"
142+
import { StandardTableView } from "std-widgets.slint";
97143
global Glob {
98144
in-out property <{a:int,b:float}> hello_world;
99145
callback cb(string, int) -> [int];
@@ -124,6 +170,9 @@ export component Test {
124170
background: red;
125171
border-color: self.background;
126172
}
173+
StandardTableView {
174+
row-pointer-event => { }
175+
}
127176
}"#;
128177
let (mut dc, uri, _) = crate::language::test::loaded_document_cache(source.into());
129178
let doc = dc.get_document(&uri).unwrap().node.clone().unwrap();
@@ -184,9 +233,14 @@ export component Test {
184233
get_tooltip(&mut dc, find_tk("Glob.cb(", 6.into())),
185234
"```slint\ncallback cb(string, int) -> [int]\n```",
186235
);
236+
assert_tooltip(
237+
get_tooltip(&mut dc, find_tk("row-pointer-event", 0.into())),
238+
// Fixme: this uses LogicalPoint instead of Point because of implementation details
239+
"```slint\ncallback row-pointer-event(row-index: int, event: PointerEvent, mouse-position: LogicalPosition)\n```",
240+
);
187241
assert_tooltip(
188242
get_tooltip(&mut dc, find_tk("fn_glob(local-prop)", 1.into())),
189-
"```slint\npure function fn-glob(int)\n```",
243+
"```slint\npure function fn-glob(abc: int)\n```",
190244
);
191245
assert_tooltip(
192246
get_tooltip(&mut dc, find_tk("root.fn_loc", 8.into())),

tools/lsp/language/token_info.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub enum TokenInfo {
2222
FileName(std::path::PathBuf),
2323
LocalProperty(syntax_nodes::PropertyDeclaration),
2424
LocalCallback(syntax_nodes::CallbackDeclaration),
25+
/// This is like a NamedReference, but the element doesn't have an ElementRc because
26+
/// its enclosing component might not have been properly parsed
2527
IncompleteNamedReference(ElementType, SmolStr),
2628
}
2729

0 commit comments

Comments
 (0)