1use crate::context::ArgumentContext;
4use crate::symbols::{SymbolInfo, symbols_to_completions};
5use crate::type_inference::unified_metadata;
6use shape_runtime::metadata::{FunctionInfo, LanguageMetadata};
7use tower_lsp_server::ls_types::{
8 CompletionItem, CompletionItemKind, Documentation, InsertTextFormat, MarkupContent, MarkupKind,
9};
10
11use super::annotations::{enum_value_completions, symbols_with_annotation};
12use super::providers::provider_completions;
13
14pub fn function_argument_completions(
16 user_symbols: &[SymbolInfo],
17 function: &str,
18 arg_context: &ArgumentContext,
19) -> Vec<CompletionItem> {
20 match arg_context {
21 ArgumentContext::FunctionArgument { arg_index, .. } => {
22 if let Some(dot_pos) = function.rfind('.') {
25 let module = &function[..dot_pos];
26 let method = &function[dot_pos + 1..];
27 if super::imports::is_extension_module(module) {
28 return super::imports::module_function_param_completions(module, method);
29 }
30 }
31
32 if let Some(completions) = content_method_arg_completions(function, *arg_index) {
34 return completions;
35 }
36
37 let meta = unified_metadata();
39 if let Some(func_info) = meta.get_function(function) {
40 if let Some(param) = func_info.parameters.get(*arg_index) {
41 if let Some(constraints) = ¶m.constraints {
43 if constraints.is_provider_name {
45 return provider_completions();
46 }
47
48 if let Some(values) = &constraints.allowed_values {
50 return enum_value_completions(values);
51 }
52
53 if let Some(annotation) = &constraints.requires_annotation {
55 return symbols_with_annotation(annotation, user_symbols);
56 }
57 }
58 }
59 }
60
61 symbols_to_completions(user_symbols)
63 }
64 ArgumentContext::ObjectLiteralValue {
65 containing_function,
66 property_name,
67 } => {
68 if let Some(func) = containing_function {
69 return object_property_value_completions(func, property_name, user_symbols);
70 }
71 symbols_to_completions(user_symbols)
72 }
73 ArgumentContext::ObjectLiteralPropertyName {
74 containing_function,
75 } => {
76 if let Some(func) = containing_function {
77 return object_property_name_completions(func);
78 }
79 vec![]
80 }
81 ArgumentContext::General => symbols_to_completions(user_symbols),
82 }
83}
84
85pub fn object_property_value_completions(
87 function: &str,
88 property_name: &str,
89 user_symbols: &[SymbolInfo],
90) -> Vec<CompletionItem> {
91 let meta = unified_metadata();
92 if let Some(func_info) = meta.get_function(function) {
93 for param in &func_info.parameters {
95 if let Some(constraints) = ¶m.constraints {
96 if let Some(properties) = &constraints.object_properties {
97 for prop_constraint in properties {
99 if prop_constraint.name == property_name {
100 if let Some(constraint) = &prop_constraint.constraint {
101 if let Some(annotation) = &constraint.requires_annotation {
103 return symbols_with_annotation(annotation, user_symbols);
104 }
105
106 if let Some(values) = &constraint.allowed_values {
108 return enum_value_completions(values);
109 }
110 }
111 }
112 }
113 }
114 }
115 }
116 }
117
118 symbols_to_completions(user_symbols)
120}
121
122pub fn object_property_name_completions(function: &str) -> Vec<CompletionItem> {
124 let meta = unified_metadata();
125 if let Some(func_info) = meta.get_function(function) {
126 for param in &func_info.parameters {
128 if let Some(constraints) = ¶m.constraints {
129 if let Some(properties) = &constraints.object_properties {
130 return properties
131 .iter()
132 .map(|prop| {
133 let required_marker = if prop.required { " (required)" } else { "" };
134
135 CompletionItem {
136 label: prop.name.clone(),
137 kind: Some(CompletionItemKind::PROPERTY),
138 detail: Some(format!("{}{}", prop.value_type, required_marker)),
139 insert_text: Some(format!("{}: ${{1}}", prop.name)),
140 insert_text_format: Some(InsertTextFormat::SNIPPET),
141 ..Default::default()
142 }
143 })
144 .collect();
145 }
146 }
147 }
148 }
149
150 vec![]
151}
152
153pub fn keyword_completions() -> Vec<CompletionItem> {
155 LanguageMetadata::keywords()
156 .into_iter()
157 .filter(|kw| is_globally_suggested_keyword(&kw.keyword))
158 .map(|kw| CompletionItem {
159 label: kw.keyword,
160 kind: Some(CompletionItemKind::KEYWORD),
161 detail: Some(kw.description.clone()),
162 documentation: Some(Documentation::String(kw.description)),
163 insert_text_format: Some(InsertTextFormat::PLAIN_TEXT),
164 ..CompletionItem::default()
165 })
166 .collect()
167}
168
169fn is_globally_suggested_keyword(keyword: &str) -> bool {
170 !matches!(
171 keyword,
172 "meta" | "pattern" | "function" | "import" | "export" | "stream"
174 | "break" | "continue" | "join" | "race" | "settle" | "any"
176 | "method" | "as" | "default"
178 | "find" | "scan" | "analyze" | "simulate" | "all" | "extend"
180 | "and" | "or" | "not" | "on"
182 | "module" | "interface" | "this" | "when"
184 )
185}
186
187fn is_removed_toplevel_function(name: &str) -> bool {
188 matches!(
189 name,
190 "length"
191 | "keys"
192 | "values"
193 | "entries"
194 | "configure_data_source"
195 | "load"
196 | "rolling_mean"
197 | "rolling_sum"
198 | "rolling_std"
199 | "rolling_min"
200 | "rolling_max"
201 )
202}
203
204pub fn builtin_function_completions() -> Vec<CompletionItem> {
207 unified_metadata()
208 .all_functions()
209 .into_iter()
210 .filter(|f| !f.comptime_only && f.implemented && !is_removed_toplevel_function(&f.name))
211 .map(function_completion_item)
212 .collect()
213}
214
215pub fn comptime_builtin_function_completions() -> Vec<CompletionItem> {
217 unified_metadata()
218 .all_functions()
219 .into_iter()
220 .filter(|f| f.comptime_only && f.implemented)
221 .map(function_completion_item)
222 .collect()
223}
224
225pub fn function_completion_item(func: &FunctionInfo) -> CompletionItem {
226 let params_snippet: Vec<String> = func
228 .parameters
229 .iter()
230 .enumerate()
231 .map(|(i, p)| format!("${{{}:{}}}", i + 1, p.name))
232 .collect();
233 let snippet = format!("{}({})", func.name, params_snippet.join(", "));
234
235 let mut doc = format!("**{}**\n\n{}\n\n", func.signature, func.description);
237 if !func.parameters.is_empty() {
238 doc.push_str("**Parameters:**\n");
239 for param in &func.parameters {
240 doc.push_str(&format!(
241 "- `{}`: {} - {}\n",
242 param.name, param.param_type, param.description
243 ));
244 }
245 }
246 if let Some(example) = &func.example {
247 doc.push_str(&format!("\n**Example:**\n```shape\n{}\n```", example));
248 }
249 if !func.implemented {
250 doc.push_str("\n\n**Status:** Not yet implemented.");
251 }
252
253 let detail = if func.implemented {
254 func.signature.clone()
255 } else {
256 format!("{} (unimplemented)", func.signature)
257 };
258
259 CompletionItem {
260 label: func.name.clone(),
261 kind: Some(CompletionItemKind::FUNCTION),
262 detail: Some(detail),
263 documentation: Some(Documentation::MarkupContent(MarkupContent {
264 kind: MarkupKind::Markdown,
265 value: doc,
266 })),
267 insert_text: Some(snippet),
268 insert_text_format: Some(InsertTextFormat::SNIPPET),
269 ..CompletionItem::default()
270 }
271}
272
273fn content_method_arg_completions(function: &str, arg_index: usize) -> Option<Vec<CompletionItem>> {
277 let method = function.rsplit('.').next().unwrap_or(function);
279
280 match (method, arg_index) {
281 ("fg" | "bg", 0) => Some(color_completions()),
282 ("border", 0) => Some(border_completions()),
283 ("chart", 0) if function.contains("Content") => Some(chart_type_completions()),
284 _ => None,
285 }
286}
287
288fn color_completions() -> Vec<CompletionItem> {
289 let colors = [
290 ("Color.red", "Red terminal color"),
291 ("Color.green", "Green terminal color"),
292 ("Color.blue", "Blue terminal color"),
293 ("Color.yellow", "Yellow terminal color"),
294 ("Color.magenta", "Magenta terminal color"),
295 ("Color.cyan", "Cyan terminal color"),
296 ("Color.white", "White terminal color"),
297 ("Color.default", "Default terminal color"),
298 ];
299 let mut items: Vec<CompletionItem> = colors
300 .into_iter()
301 .map(|(label, doc)| CompletionItem {
302 label: label.to_string(),
303 kind: Some(CompletionItemKind::ENUM_MEMBER),
304 detail: Some("Color".to_string()),
305 documentation: Some(Documentation::String(doc.to_string())),
306 ..CompletionItem::default()
307 })
308 .collect();
309
310 items.push(CompletionItem {
311 label: "Color.rgb".to_string(),
312 kind: Some(CompletionItemKind::METHOD),
313 detail: Some("Color".to_string()),
314 documentation: Some(Documentation::String(
315 "Custom RGB color (0-255 per channel)".to_string(),
316 )),
317 insert_text: Some("Color.rgb(${1:r}, ${2:g}, ${3:b})".to_string()),
318 insert_text_format: Some(InsertTextFormat::SNIPPET),
319 ..CompletionItem::default()
320 });
321
322 items
323}
324
325fn border_completions() -> Vec<CompletionItem> {
326 [
327 ("Border.rounded", "Rounded corners (default)"),
328 ("Border.sharp", "Sharp 90-degree corners"),
329 ("Border.heavy", "Thick border lines"),
330 ("Border.double", "Double-line border"),
331 ("Border.minimal", "Minimal separator lines"),
332 ("Border.none", "No border"),
333 ]
334 .into_iter()
335 .map(|(label, doc)| CompletionItem {
336 label: label.to_string(),
337 kind: Some(CompletionItemKind::ENUM_MEMBER),
338 detail: Some("Border".to_string()),
339 documentation: Some(Documentation::String(doc.to_string())),
340 ..CompletionItem::default()
341 })
342 .collect()
343}
344
345fn chart_type_completions() -> Vec<CompletionItem> {
346 [
347 ("ChartType.line", "Line chart"),
348 ("ChartType.bar", "Bar chart"),
349 ("ChartType.scatter", "Scatter plot"),
350 ("ChartType.area", "Area chart"),
351 ("ChartType.candlestick", "Candlestick chart"),
352 ("ChartType.histogram", "Histogram"),
353 ]
354 .into_iter()
355 .map(|(label, doc)| CompletionItem {
356 label: label.to_string(),
357 kind: Some(CompletionItemKind::ENUM_MEMBER),
358 detail: Some("ChartType".to_string()),
359 documentation: Some(Documentation::String(doc.to_string())),
360 ..CompletionItem::default()
361 })
362 .collect()
363}