nifty_lsp/features/
signature_help.rs1use lsp_types::{
4 Documentation, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
5};
6
7use crate::context;
8use crate::document::to_u32;
9use crate::project::Cursor;
10use crate::signature;
11
12pub(crate) fn signature_help(cursor: Cursor) -> Option<SignatureHelp> {
13 let document = cursor.document();
14 let word_start = document.word_start(cursor.offset);
15 let call = context::at(document, word_start).call?;
16 let signature = signature::of(cursor.project, call.callee.as_deref()?)?;
17 let active = signature.active(call.label.as_deref(), call.positional);
18 let (label, offsets) = signature.label();
19 let parameters = signature
20 .params
21 .iter()
22 .zip(offsets)
23 .map(|(param, offset)| ParameterInformation {
24 label: ParameterLabel::LabelOffsets(offset),
25 documentation: (!param.positional)
26 .then(|| Documentation::String(format!("Named: `{}: …`", param.name))),
27 })
28 .collect();
29 Some(SignatureHelp {
30 signatures: vec![SignatureInformation {
31 label,
32 documentation: signature
33 .summary
34 .map(|summary| Documentation::String(summary.to_owned())),
35 parameters: Some(parameters),
36 active_parameter: active.map(to_u32),
37 }],
38 active_signature: Some(0),
39 active_parameter: active.map(to_u32),
40 })
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::project::Project;
47
48 fn help(source: &str) -> Option<(String, Option<u32>)> {
50 let (project, offset) = Project::with_cursor(source);
51 let help = signature_help(project.cursor(offset))?;
52 Some((help.signatures[0].label.clone(), help.active_parameter))
53 }
54
55 #[test]
56 fn shows_an_elements_parameters_and_the_one_being_filled() {
57 let window = "window(title: fixed string, width: number, height: number, backdrop: backdrop, open: bool)";
58 assert_eq!(help("window(|"), Some((window.to_owned(), Some(0))));
59 assert_eq!(
60 help("window settings(\"Settings\", |"),
61 Some((window.to_owned(), None)),
62 "a named window is still a window"
63 );
64 assert_eq!(help("window(\"App\", |"), Some((window.to_owned(), None)));
65 assert_eq!(
66 help("window(\"App\", height: |"),
67 Some((window.to_owned(), Some(2)))
68 );
69 }
70
71 #[test]
72 fn shows_declared_callables_and_stays_inside_nested_brackets() {
73 let source = "fn add(a: int, b: [int]) -> int { return a }\nlet x = add(1, [|";
74 assert_eq!(
75 help(source),
76 Some(("add(a: int, b: [int])".to_owned(), Some(1)))
77 );
78 }
79
80 #[test]
81 fn shows_nothing_outside_a_call() {
82 assert_eq!(help("state x = |"), None);
83 }
84}