KCL: Customizable per-arg and per-fn snippet values (#7156)

Before, the LSP snippet for `startProfile` was 

```
startProfile(%, at = [3.14, 3.14])
```

Now it's 

```
startProfile(%, at = [0, 0])
```

This is configured by adding a `snippet_value=` field to the stdlib macro. For example:


```diff
#[stdlib {
    name = "startProfile",
    keywords = true,
    unlabeled_first = true,
    args = {
        sketch_surface = { docs = "What to start the profile on" },
-       at = { docs = "Where to start the profile. An absolute point." },
+       at = { docs = "Where to start the profile. An absolute point.", snippet_value = "[0, 0]" },        tag = { docs = "Tag this first starting point" },
    },
    tags = ["sketch"]
}]
```

## Work for follow-up PRs

 - Make this work for KCL functions defined in KCL, e.g. [`fn circle`](36c8ad439d/rust/kcl-lib/std/sketch.kcl (L31-L32)) -- something like `@(snippet_value = "[0, 0]")` perhaps
 - Go through the stdlib and change defaults where appropriate
This commit is contained in:
Adam Chalmers
2025-05-21 15:18:20 -05:00
committed by GitHub
parent a36530d6df
commit 0ea1e9a6da
19 changed files with 140 additions and 101 deletions

View File

@ -111,6 +111,13 @@ pub struct StdLibFnArg {
/// Include this in completion snippets?
#[serde(default, skip_serializing_if = "is_false")]
pub include_in_snippet: bool,
/// Snippet should suggest this value for the argument.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub snippet_value: Option<String>,
/// Snippet should suggest this value for the argument.
/// The suggested value should be an array, with these elements.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub snippet_value_array: Option<Vec<String>>,
/// Additional information that could be used instead of the type's description.
/// This is helpful if the type is really basic, like "u32" -- that won't tell the user much about
/// how this argument is meant to be used.
@ -165,6 +172,21 @@ impl StdLibFnArg {
} else {
""
};
if let Some(vals) = &self.snippet_value_array {
let mut snippet = label.to_owned();
snippet.push('[');
for (i, val) in vals.iter().enumerate() {
snippet.push_str(&format!("${{{}:{}}}", index + i, val));
if i != vals.len() - 1 {
snippet.push_str(", ");
}
}
snippet.push(']');
return Ok(Some((index + vals.len(), snippet)));
}
if let Some(val) = &self.snippet_value {
return Ok(Some((index, format!("{label}${{{}:{}}}", index, val))));
}
if (self.type_ == "Sketch"
|| self.type_ == "[Sketch]"
|| self.type_ == "Geometry"
@ -988,6 +1010,13 @@ mod tests {
assert_eq!(snippet, r#"startSketchOn(${0:XY})"#);
}
#[test]
fn get_autocomplete_snippet_start_profile() {
let start_sketch_on_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::StartProfile);
let snippet = start_sketch_on_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"startProfile(${0:%}, at = [${1:0}, ${2:0}])"#);
}
#[test]
fn get_autocomplete_snippet_pattern_circular_3d() {
// We test this one specifically because it has ints and floats and strings.