1use super::*;
2
3#[derive(Debug)]
4pub enum Builtin<'a> {
5 Attribute {
6 name: &'a str,
7 kind: AttributeKind,
8 description: &'a str,
9 targets: &'a [AttributeTarget],
10 },
11 Constant {
12 name: &'a str,
13 description: &'a str,
14 },
15 Function {
16 name: &'a str,
17 aliases: &'a [&'a str],
18 kind: FunctionKind,
19 description: &'a str,
20 deprecated: Option<&'a str>,
21 },
22 Setting {
23 name: &'a str,
24 kind: SettingKind,
25 description: &'a str,
26 deprecated: Option<&'a str>,
27 },
28}
29
30impl Builtin<'_> {
31 #[must_use]
32 pub fn completion_items(&self) -> Vec<lsp::CompletionItem> {
33 match self {
34 Self::Attribute { name, .. } => vec![lsp::CompletionItem {
35 label: name.to_string(),
36 kind: Some(lsp::CompletionItemKind::KEYWORD),
37 documentation: Some(lsp::Documentation::MarkupContent(
38 self.description(),
39 )),
40 insert_text: Some(name.to_string()),
41 insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
42 sort_text: Some(format!("z{name}")),
43 ..Default::default()
44 }],
45 Self::Constant { name, .. } => vec![lsp::CompletionItem {
46 label: name.to_string(),
47 kind: Some(lsp::CompletionItemKind::CONSTANT),
48 documentation: Some(lsp::Documentation::MarkupContent(
49 self.description(),
50 )),
51 insert_text: Some(name.to_string()),
52 insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
53 sort_text: Some(format!("z{name}")),
54 ..Default::default()
55 }],
56 Self::Function { name, aliases, .. } => once(*name)
57 .chain(aliases.iter().copied())
58 .map(|name| self.function_completion_item(name))
59 .collect(),
60 Self::Setting { name, .. } => vec![lsp::CompletionItem {
61 label: name.to_string(),
62 kind: Some(lsp::CompletionItemKind::PROPERTY),
63 documentation: Some(lsp::Documentation::MarkupContent(
64 self.description(),
65 )),
66 insert_text: Some(name.to_string()),
67 insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
68 sort_text: Some(format!("z{name}")),
69 ..Default::default()
70 }],
71 }
72 }
73
74 #[must_use]
75 pub fn description(&self) -> lsp::MarkupContent {
76 lsp::MarkupContent {
77 kind: lsp::MarkupKind::Markdown,
78 value: (match self {
79 Self::Attribute { description, .. }
80 | Self::Constant { description, .. }
81 | Self::Function { description, .. }
82 | Self::Setting { description, .. } => description,
83 })
84 .to_string(),
85 }
86 }
87
88 fn function_completion_item(&self, name: &str) -> lsp::CompletionItem {
89 let snippet = match name {
90 "absolute_path" | "blake3_file" | "canonicalize" | "clean"
91 | "extension" | "file_name" | "file_stem" | "parent_dir"
92 | "parent_directory" | "path_exists" | "read" | "sha256_file"
93 | "without_extension" => {
94 format!("{name}(${{1:path:string}})")
95 }
96 "append" => {
97 format!("{name}(${{1:suffix:string}}, ${{2:s:string}})")
98 }
99 "arch"
100 | "num_cpus"
101 | "os"
102 | "os_family"
103 | "is_dependency"
104 | "invocation_directory"
105 | "invocation_directory_native"
106 | "invocation_dir"
107 | "invocation_dir_native"
108 | "justfile"
109 | "justfile_directory"
110 | "justfile_dir"
111 | "module_file"
112 | "module_directory"
113 | "module_dir"
114 | "module_path"
115 | "source_file"
116 | "source_directory"
117 | "source_dir"
118 | "just_executable"
119 | "just_pid"
120 | "uuid"
121 | "runtime_directory"
122 | "runtime_dir"
123 | "cache_directory"
124 | "cache_dir"
125 | "config_directory"
126 | "config_dir"
127 | "config_local_directory"
128 | "config_local_dir"
129 | "data_directory"
130 | "data_dir"
131 | "data_local_directory"
132 | "data_local_dir"
133 | "executable_directory"
134 | "executable_dir"
135 | "home_directory"
136 | "home_dir" => format!("{name}()"),
137 "blake3" | "sha256" => format!("{name}(${{1:string:string}})"),
138 "capitalize"
139 | "encode_uri_component"
140 | "kebabcase"
141 | "lowercase"
142 | "lowercamelcase"
143 | "quote"
144 | "shoutykebabcase"
145 | "shoutysnakecase"
146 | "snakecase"
147 | "titlecase"
148 | "trim"
149 | "trim_end"
150 | "trim_start"
151 | "uppercamelcase"
152 | "uppercase" => format!("{name}(${{1:s:string}})"),
153 "choose" => {
154 format!("{name}(${{1:n:string}}, ${{2:alphabet:string}})")
155 }
156 "datetime" | "datetime_utc" => {
157 format!("{name}(${{1:format:string}})")
158 }
159 "env" => {
160 format!("{name}(${{1:key:string}}${{2:, default:string}})")
161 }
162 "env_var" => format!("{name}(${{1:key:string}})"),
163 "env_var_or_default" => {
164 format!("{name}(${{1:key:string}}, ${{2:default:string}})")
165 }
166 "error" => format!("{name}(${{1:message:string}})"),
167 "join" => format!(
168 "{name}(${{1:a:string}}, ${{2:b:string}}${{3:, more:string...}})",
169 ),
170 "prepend" => {
171 format!("{name}(${{1:prefix:string}}, ${{2:s:string}})")
172 }
173 "replace" => {
174 format!("{name}(${{1:s:string}}, ${{2:from:string}}, ${{3:to:string}})")
175 }
176 "replace_regex" => {
177 format!(
178 "{name}(${{1:s:string}}, ${{2:regex:string}}, ${{3:replacement:string}})"
179 )
180 }
181 "require" | "style" | "which" => {
182 format!("{name}(${{1:name:string}})")
183 }
184 "semver_matches" => {
185 format!("{name}(${{1:version:string}}, ${{2:requirement:string}})")
186 }
187 "shell" => {
188 format!("{name}(${{1:command:string}}${{2:, args:string...}})")
189 }
190 "trim_end_match" | "trim_end_matches" | "trim_start_match"
191 | "trim_start_matches" => {
192 format!("{name}(${{1:s:string}}, ${{2:substring:string}})")
193 }
194 _ => format!("{name}(${{1:}})"),
195 };
196
197 lsp::CompletionItem {
198 label: name.to_string(),
199 kind: Some(lsp::CompletionItemKind::FUNCTION),
200 documentation: Some(lsp::Documentation::MarkupContent(
201 self.description(),
202 )),
203 insert_text: Some(snippet),
204 insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
205 sort_text: Some(format!("z{name}")),
206 ..Default::default()
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn fallback_function_completion_snippet() {
217 let items = Builtin::Function {
218 name: "foo",
219 aliases: &[],
220 kind: FunctionKind::Nullary,
221 description: "",
222 deprecated: None,
223 }
224 .completion_items();
225
226 assert_eq!(
227 items,
228 vec![lsp::CompletionItem {
229 label: "foo".into(),
230 kind: Some(lsp::CompletionItemKind::FUNCTION),
231 documentation: Some(lsp::Documentation::MarkupContent(
232 lsp::MarkupContent {
233 kind: lsp::MarkupKind::Markdown,
234 value: String::new()
235 },
236 )),
237 insert_text: Some("foo(${1:})".into()),
238 insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
239 sort_text: Some("zfoo".into()),
240 ..Default::default()
241 }],
242 );
243 }
244
245 #[test]
246 fn function_alias_uses_alias_snippet() {
247 let items = Builtin::Function {
248 name: "home_directory",
249 aliases: &["home_dir"],
250 kind: FunctionKind::Nullary,
251 description: "bar",
252 deprecated: None,
253 }
254 .completion_items();
255
256 assert_eq!(
257 items,
258 vec![
259 lsp::CompletionItem {
260 label: "home_directory".into(),
261 kind: Some(lsp::CompletionItemKind::FUNCTION),
262 documentation: Some(lsp::Documentation::MarkupContent(
263 lsp::MarkupContent {
264 kind: lsp::MarkupKind::Markdown,
265 value: "bar".into(),
266 },
267 )),
268 insert_text: Some("home_directory()".into()),
269 insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
270 sort_text: Some("zhome_directory".into()),
271 ..Default::default()
272 },
273 lsp::CompletionItem {
274 label: "home_dir".into(),
275 kind: Some(lsp::CompletionItemKind::FUNCTION),
276 documentation: Some(lsp::Documentation::MarkupContent(
277 lsp::MarkupContent {
278 kind: lsp::MarkupKind::Markdown,
279 value: "bar".into(),
280 },
281 )),
282 insert_text: Some("home_dir()".into()),
283 insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
284 sort_text: Some("zhome_dir".into()),
285 ..Default::default()
286 },
287 ],
288 );
289 }
290}