Skip to main content

nifty_lsp/
signature.rs

1//! The parameters of whatever a call names: a built-in element or function, or a function,
2//! view or struct declared in any file of the project.
3
4use nifty_check::elements::{self, ParamKind};
5use nifty_check::functions;
6use nifty_syntax::ast::{Item, TypedName};
7
8use crate::document::to_u32;
9use crate::project::Project;
10
11#[derive(Debug)]
12pub(crate) struct Signature {
13    pub name: String,
14    pub params: Vec<Param>,
15    pub summary: Option<&'static str>,
16}
17
18#[derive(Debug)]
19pub(crate) struct Param {
20    pub name: String,
21    /// How the expected value reads, such as `number` or `[Todo]`.
22    pub detail: String,
23    /// Whether it may be given without a label. Declared callables accept either form;
24    /// built-ins take each parameter in exactly one.
25    pub positional: bool,
26    pub named: bool,
27    /// The built-in parameter's kind; `None` for declared callables.
28    pub kind: Option<ParamKind>,
29}
30
31impl Signature {
32    /// `name(param: detail, …)`, with each parameter's place in it.
33    pub(crate) fn label(&self) -> (String, Vec<[u32; 2]>) {
34        let mut label = format!("{}(", self.name);
35        let mut offsets = Vec::with_capacity(self.params.len());
36        for (index, param) in self.params.iter().enumerate() {
37            if index > 0 {
38                label.push_str(", ");
39            }
40            let start = label.encode_utf16().count();
41            label.push_str(&param.name);
42            label.push_str(": ");
43            label.push_str(&param.detail);
44            offsets.push([to_u32(start), to_u32(label.encode_utf16().count())]);
45        }
46        label.push(')');
47        (label, offsets)
48    }
49
50    /// The parameter an argument fills: the one its label names, or else the next one that
51    /// may be given by position.
52    pub(crate) fn active(&self, label: Option<&str>, positional: usize) -> Option<usize> {
53        match label {
54            Some(label) => self.params.iter().position(|param| param.name == label),
55            None => self
56                .params
57                .iter()
58                .enumerate()
59                .filter(|(_, param)| param.positional)
60                .nth(positional)
61                .map(|(index, _)| index),
62        }
63    }
64}
65
66pub(crate) fn of(project: &Project, callee: &str) -> Option<Signature> {
67    if let Some(element) = elements::element(callee) {
68        return Some(built_in(element.name, element.params, element.summary));
69    }
70    if let Some(function) = functions::function(callee) {
71        return Some(built_in(function.name, function.params, function.summary));
72    }
73    let params = project.items().find_map(|item| match item {
74        Item::Fn(decl) if decl.name.name == callee => Some(&decl.params),
75        Item::View(decl) if decl.name.name == callee => Some(&decl.params),
76        Item::Struct(decl) if decl.name.name == callee => Some(&decl.fields),
77        _ => None,
78    })?;
79    Some(Signature {
80        name: callee.to_owned(),
81        params: params
82            .iter()
83            .map(|param| declared(project, param))
84            .collect(),
85        summary: None,
86    })
87}
88
89fn built_in(name: &str, params: &[elements::Param], summary: &'static str) -> Signature {
90    Signature {
91        name: name.to_owned(),
92        params: params
93            .iter()
94            .map(|param| Param {
95                name: param.name.to_owned(),
96                detail: param.kind.describe().into_owned(),
97                positional: param.positional,
98                named: !param.positional,
99                kind: Some(param.kind),
100            })
101            .collect(),
102        summary: Some(summary),
103    }
104}
105
106fn declared(project: &Project, param: &TypedName) -> Param {
107    Param {
108        name: param.name.name.clone(),
109        detail: project.text(param.ty.span).to_owned(),
110        positional: true,
111        named: true,
112        kind: None,
113    }
114}