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}