diff --git a/docs/kcl-std/std.json b/docs/kcl-std/std.json index 794d6d54d..099d62850 100644 --- a/docs/kcl-std/std.json +++ b/docs/kcl-std/std.json @@ -207425,6 +207425,10 @@ }, "required": true, "includeInSnippet": true, + "snippetValueArray": [ + "0", + "0" + ], "description": "Where to start the profile. An absolute point.", "labelRequired": true }, diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index dbf0b5575..94af2bc88 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -1001,7 +1001,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10)) await expect(page.locator('.cm-content')).toHaveText( `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) - |> startProfile(%, at = [3.14, 12]) + |> startProfile(%, at = [0, 12]) |> xLine(%, length = 5) // lin`.replaceAll('\n', '') ) @@ -1076,7 +1076,7 @@ sketch001 = startSketchOn(XZ) await expect(page.locator('.cm-content')).toHaveText( `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) - |> startProfile(%, at = [3.14, 12]) + |> startProfile(%, at = [0, 12]) |> xLine(%, length = 5) // lin`.replaceAll('\n', '') ) }) diff --git a/rust/kcl-derive-docs/src/lib.rs b/rust/kcl-derive-docs/src/lib.rs index 6b9093c29..e731b33a2 100644 --- a/rust/kcl-derive-docs/src/lib.rs +++ b/rust/kcl-derive-docs/src/lib.rs @@ -47,6 +47,14 @@ struct ArgMetadata { /// Does not do anything if the argument is already required. #[serde(default)] include_in_snippet: bool, + + /// The snippet should suggest this value for the arg. + #[serde(default)] + snippet_value: Option, + + /// The snippet should suggest this value for the arg. + #[serde(default)] + snippet_value_array: Option>, } #[derive(Deserialize, Debug)] @@ -318,6 +326,10 @@ fn do_stdlib_inner( } .trim_start_matches('_') .to_string(); + // These aren't really KCL args, they're just state that each stdlib function's impl needs. + if arg_name == "exec_state" || arg_name == "args" { + continue; + } let ty = match arg { syn::FnArg::Receiver(pat) => pat.ty.as_ref().into_token_stream(), @@ -328,27 +340,24 @@ fn do_stdlib_inner( let ty_string = rust_type_to_openapi_type(&ty_string); let required = !ty_ident.to_string().starts_with("Option <"); - let arg_meta = metadata.args.get(&arg_name); - let description = if let Some(s) = arg_meta.map(|arg| &arg.docs) { - quote! { #s } - } else if metadata.keywords && ty_string != "Args" && ty_string != "ExecState" { - errors.push(Error::new_spanned( - &arg, - "Argument was not documented in the args block", - )); + let Some(arg_meta) = metadata.args.get(&arg_name) else { + errors.push(Error::new_spanned(arg, format!("arg {arg_name} not found"))); continue; - } else { - quote! { String::new() } }; - let include_in_snippet = required || arg_meta.map(|arg| arg.include_in_snippet).unwrap_or_default(); + let description = arg_meta.docs.clone(); + let include_in_snippet = required || arg_meta.include_in_snippet; + let snippet_value = arg_meta.snippet_value.clone(); + let snippet_value_array = arg_meta.snippet_value_array.clone(); + if snippet_value.is_some() && snippet_value_array.is_some() { + errors.push(Error::new_spanned(arg, format!("arg {arg_name} has set both snippet_value and snippet_value array, but at most one of these may be set. Please delete one of them."))); + } let label_required = !(i == 0 && metadata.unlabeled_first); let camel_case_arg_name = to_camel_case(&arg_name); if ty_string != "ExecState" && ty_string != "Args" { let schema = quote! { generator.root_schema_for::<#ty_ident>() }; - arg_types.push(quote! { - #docs_crate::StdLibFnArg { + let q0 = quote! { name: #camel_case_arg_name.to_string(), type_: #ty_string.to_string(), schema: #schema, @@ -356,6 +365,32 @@ fn do_stdlib_inner( label_required: #label_required, description: #description.to_string(), include_in_snippet: #include_in_snippet, + }; + let q1 = if let Some(snippet_value) = snippet_value { + quote! { + snippet_value: Some(#snippet_value.to_owned()), + } + } else { + quote! { + snippet_value: None, + } + }; + let q2 = if let Some(snippet_value_array) = snippet_value_array { + quote! { + snippet_value_array: Some(vec![ + #(#snippet_value_array.to_owned()),* + ]), + } + } else { + quote! { + snippet_value_array: None, + } + }; + arg_types.push(quote! { + #docs_crate::StdLibFnArg { + #q0 + #q1 + #q2 } }); } @@ -419,6 +454,8 @@ fn do_stdlib_inner( label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } } else { diff --git a/rust/kcl-derive-docs/src/tests.rs b/rust/kcl-derive-docs/src/tests.rs index f5ffbc831..6567ccc74 100644 --- a/rust/kcl-derive-docs/src/tests.rs +++ b/rust/kcl-derive-docs/src/tests.rs @@ -40,6 +40,9 @@ fn test_args_with_refs() { let (item, mut errors) = do_stdlib( quote! { name = "someFn", + args = { + data = { docs = "The data for this function"}, + }, }, quote! { /// Docs @@ -65,6 +68,9 @@ fn test_args_with_lifetime() { let (item, mut errors) = do_stdlib( quote! { name = "someFn", + args = { + data = { docs = "Arg for the function" }, + } }, quote! { /// Docs @@ -117,7 +123,8 @@ fn test_stdlib_line_to() { quote! { name = "lineTo", args = { - sketch = { docs = "the sketch you're adding the line to" } + data = { docs = "the sketch you're adding the line to" }, + sketch = { docs = "the sketch you're adding the line to" }, } }, quote! { diff --git a/rust/kcl-derive-docs/tests/args_with_lifetime.gen b/rust/kcl-derive-docs/tests/args_with_lifetime.gen index d60d3aa2b..475c94444 100644 --- a/rust/kcl-derive-docs/tests/args_with_lifetime.gen +++ b/rust/kcl-derive-docs/tests/args_with_lifetime.gen @@ -105,8 +105,10 @@ impl crate::docs::StdLibFn for SomeFn { schema: generator.root_schema_for::(), required: true, label_required: true, - description: String::new().to_string(), + description: "Arg for the function".to_string(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }] } @@ -123,6 +125,8 @@ impl crate::docs::StdLibFn for SomeFn { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/args_with_refs.gen b/rust/kcl-derive-docs/tests/args_with_refs.gen index 758e97e45..06ed0bfbf 100644 --- a/rust/kcl-derive-docs/tests/args_with_refs.gen +++ b/rust/kcl-derive-docs/tests/args_with_refs.gen @@ -105,8 +105,10 @@ impl crate::docs::StdLibFn for SomeFn { schema: generator.root_schema_for::(), required: true, label_required: true, - description: String::new().to_string(), + description: "The data for this function".to_string(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }] } @@ -123,6 +125,8 @@ impl crate::docs::StdLibFn for SomeFn { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/array.gen b/rust/kcl-derive-docs/tests/array.gen index 16f17e7eb..c03bb55cc 100644 --- a/rust/kcl-derive-docs/tests/array.gen +++ b/rust/kcl-derive-docs/tests/array.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Show { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "[number]".to_string(), - schema: generator.root_schema_for::<[f64; 2usize]>(), - required: true, - label_required: true, - description: String::new().to_string(), - include_in_snippet: true, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Show { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/box.gen b/rust/kcl-derive-docs/tests/box.gen index cdd591220..f2d136cc2 100644 --- a/rust/kcl-derive-docs/tests/box.gen +++ b/rust/kcl-derive-docs/tests/box.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Show { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "number".to_string(), - schema: generator.root_schema_for::(), - required: true, - label_required: true, - description: String::new().to_string(), - include_in_snippet: true, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Show { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/doc_comment_with_code.gen b/rust/kcl-derive-docs/tests/doc_comment_with_code.gen index 29b64ba56..07a816f90 100644 --- a/rust/kcl-derive-docs/tests/doc_comment_with_code.gen +++ b/rust/kcl-derive-docs/tests/doc_comment_with_code.gen @@ -101,15 +101,7 @@ impl crate::docs::StdLibFn for MyFunc { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "kittycad::types::InputFormat".to_string(), - schema: generator.root_schema_for::>(), - required: false, - label_required: true, - description: String::new().to_string(), - include_in_snippet: false, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -125,6 +117,8 @@ impl crate::docs::StdLibFn for MyFunc { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/lineTo.gen b/rust/kcl-derive-docs/tests/lineTo.gen index 5a2b33f84..c34c04c45 100644 --- a/rust/kcl-derive-docs/tests/lineTo.gen +++ b/rust/kcl-derive-docs/tests/lineTo.gen @@ -108,8 +108,10 @@ impl crate::docs::StdLibFn for LineTo { schema: generator.root_schema_for::(), required: true, label_required: true, - description: String::new().to_string(), + description: "the sketch you're adding the line to".to_string(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }, crate::docs::StdLibFnArg { name: "sketch".to_string(), @@ -119,6 +121,8 @@ impl crate::docs::StdLibFn for LineTo { label_required: true, description: "the sketch you're adding the line to".to_string(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }, ] } @@ -136,6 +140,8 @@ impl crate::docs::StdLibFn for LineTo { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/min.gen b/rust/kcl-derive-docs/tests/min.gen index 39d7f7e31..4ff3f6344 100644 --- a/rust/kcl-derive-docs/tests/min.gen +++ b/rust/kcl-derive-docs/tests/min.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Min { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "[number]".to_string(), - schema: generator.root_schema_for::>(), - required: true, - label_required: true, - description: String::new().to_string(), - include_in_snippet: true, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Min { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/option.gen b/rust/kcl-derive-docs/tests/option.gen index 83073cda7..9bab9c59e 100644 --- a/rust/kcl-derive-docs/tests/option.gen +++ b/rust/kcl-derive-docs/tests/option.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Show { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "number".to_string(), - schema: generator.root_schema_for::>(), - required: false, - label_required: true, - description: String::new().to_string(), - include_in_snippet: false, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Show { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/option_input_format.gen b/rust/kcl-derive-docs/tests/option_input_format.gen index 4ea47f578..47f23f2b3 100644 --- a/rust/kcl-derive-docs/tests/option_input_format.gen +++ b/rust/kcl-derive-docs/tests/option_input_format.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Import { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "kittycad::types::InputFormat".to_string(), - schema: generator.root_schema_for::>(), - required: false, - label_required: true, - description: String::new().to_string(), - include_in_snippet: false, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Import { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/return_vec_box_sketch.gen b/rust/kcl-derive-docs/tests/return_vec_box_sketch.gen index aa52d82d0..6b8aa3549 100644 --- a/rust/kcl-derive-docs/tests/return_vec_box_sketch.gen +++ b/rust/kcl-derive-docs/tests/return_vec_box_sketch.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Import { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "kittycad::types::InputFormat".to_string(), - schema: generator.root_schema_for::>(), - required: false, - label_required: true, - description: String::new().to_string(), - include_in_snippet: false, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Import { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/return_vec_sketch.gen b/rust/kcl-derive-docs/tests/return_vec_sketch.gen index 506516384..bfd9f5e53 100644 --- a/rust/kcl-derive-docs/tests/return_vec_sketch.gen +++ b/rust/kcl-derive-docs/tests/return_vec_sketch.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Import { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "kittycad::types::InputFormat".to_string(), - schema: generator.root_schema_for::>(), - required: false, - label_required: true, - description: String::new().to_string(), - include_in_snippet: false, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Import { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/show.gen b/rust/kcl-derive-docs/tests/show.gen index 73aa9c48a..4fa5da755 100644 --- a/rust/kcl-derive-docs/tests/show.gen +++ b/rust/kcl-derive-docs/tests/show.gen @@ -100,15 +100,7 @@ impl crate::docs::StdLibFn for Show { let mut settings = schemars::gen::SchemaSettings::openapi3(); settings.inline_subschemas = inline_subschemas; let mut generator = schemars::gen::SchemaGenerator::new(settings); - vec![crate::docs::StdLibFnArg { - name: "args".to_string(), - type_: "[number]".to_string(), - schema: generator.root_schema_for::>(), - required: true, - label_required: true, - description: String::new().to_string(), - include_in_snippet: true, - }] + vec![] } fn return_value(&self, inline_subschemas: bool) -> Option { @@ -124,6 +116,8 @@ impl crate::docs::StdLibFn for Show { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-derive-docs/tests/test_args_with_exec_state.gen b/rust/kcl-derive-docs/tests/test_args_with_exec_state.gen index 2457d1057..6950babdf 100644 --- a/rust/kcl-derive-docs/tests/test_args_with_exec_state.gen +++ b/rust/kcl-derive-docs/tests/test_args_with_exec_state.gen @@ -115,6 +115,8 @@ impl crate::docs::StdLibFn for SomeFunction { label_required: true, description: String::new(), include_in_snippet: true, + snippet_value: None, + snippet_value_array: None, }) } diff --git a/rust/kcl-lib/src/docs/mod.rs b/rust/kcl-lib/src/docs/mod.rs index 34115b0e5..726cba618 100644 --- a/rust/kcl-lib/src/docs/mod.rs +++ b/rust/kcl-lib/src/docs/mod.rs @@ -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, + /// 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>, /// 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 = 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. diff --git a/rust/kcl-lib/src/std/sketch.rs b/rust/kcl-lib/src/std/sketch.rs index 75585e4b6..a2019f2a8 100644 --- a/rust/kcl-lib/src/std/sketch.rs +++ b/rust/kcl-lib/src/std/sketch.rs @@ -1322,7 +1322,7 @@ pub async fn start_profile(exec_state: &mut ExecState, args: Args) -> Result