Skip to main content

lumen_script/
builtins.rs

1//! Builtin-function metadata: the shape of one entry in a host's table.
2//!
3//! Each script host keeps a table of the builtins it registers, spelled the
4//! way its own language names their types, and hands it out through
5//! [`ScriptHost::builtins`](crate::ScriptHost::builtins). Editor tooling
6//! (`lumen-lsp`) reads it for completion, hover, and signature help.
7
8/// One parameter of a builtin function.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct BuiltinParam {
11    /// Parameter name, used for signature help and snippet placeholders.
12    pub name: &'static str,
13    /// Human-readable script-side type (`string`, `int`, `float`,
14    /// `bool`, `array`, `fn`, `any`).
15    pub ty: &'static str,
16}
17
18/// One Lumen script builtin: its name, parameters, return type, and a
19/// one-line documentation string.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct BuiltinFn {
22    /// Function name as called from script.
23    pub name: &'static str,
24    /// Ordered parameter list.
25    pub params: &'static [BuiltinParam],
26    /// Return type (`()` for commands that return nothing).
27    pub ret: &'static str,
28    /// One-line summary shown in completion detail and hover.
29    pub doc: &'static str,
30}
31
32impl BuiltinFn {
33    /// Render a human-readable signature, e.g.
34    /// `set_timeout(name: string, ms: int) -> ()`.
35    pub fn signature(&self) -> String {
36        let params = self
37            .params
38            .iter()
39            .map(|p| format!("{}: {}", p.name, p.ty))
40            .collect::<Vec<_>>()
41            .join(", ");
42        format!("{}({}) -> {}", self.name, params, self.ret)
43    }
44
45    /// Render an LSP snippet insert string with tab-stops for each
46    /// parameter, e.g. `set_timeout(${1:name}, ${2:ms})`.
47    pub fn snippet(&self) -> String {
48        if self.params.is_empty() {
49            return format!("{}()", self.name);
50        }
51        let params = self
52            .params
53            .iter()
54            .enumerate()
55            .map(|(i, p)| format!("${{{}:{}}}", i + 1, p.name))
56            .collect::<Vec<_>>()
57            .join(", ");
58        format!("{}({})", self.name, params)
59    }
60
61    /// Render markdown documentation for hover: a fenced signature line
62    /// followed by the doc summary.
63    ///
64    /// `lang` is the fence language, so each host labels its own buffers
65    /// with its language name.
66    pub fn hover_markdown(&self, lang: &str) -> String {
67        format!("```{lang}\n{}\n```\n\n{}", self.signature(), self.doc)
68    }
69}
70
71/// Look up a builtin by exact name in one host's table.
72#[must_use]
73pub fn lookup_in(table: &'static [BuiltinFn], name: &str) -> Option<&'static BuiltinFn> {
74    table.iter().find(|b| b.name == name)
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn no_arg_snippet() {
83        let b = BuiltinFn {
84            name: "tick",
85            params: &[],
86            ret: "()",
87            doc: "",
88        };
89        assert_eq!(b.snippet(), "tick()");
90    }
91
92    #[test]
93    fn hover_fence_takes_the_language() {
94        let b = BuiltinFn {
95            name: "page_current",
96            params: &[],
97            ret: "string",
98            doc: "The active page key.",
99        };
100        assert!(b.hover_markdown("toy").starts_with("```toy\n"));
101        assert!(b.hover_markdown("candela").contains("The active page key."));
102    }
103}