better docs on solids and sketches (#5428)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix docs;

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* parens

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* parens

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2025-02-19 19:48:27 -08:00
committed by GitHub
parent f35cd3ef26
commit 099c48cd63
20 changed files with 957 additions and 434 deletions

View File

@ -95,6 +95,28 @@ fn init_handlebars() -> Result<handlebars::Handlebars<'static>> {
),
);
hbs.register_helper(
"firstLine",
Box::new(
|h: &handlebars::Helper,
_: &handlebars::Handlebars,
_: &handlebars::Context,
_: &mut handlebars::RenderContext,
out: &mut dyn handlebars::Output|
-> handlebars::HelperResult {
// Get the first parameter passed to the helper
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
// Get the first line using lines() iterator
let first = param.lines().next().unwrap_or("");
// Write the result
out.write(first)?;
Ok(())
},
),
);
hbs.register_helper(
"neq",
Box::new(
@ -566,6 +588,10 @@ fn generate_type(
}));
}
// Cleanup the description.
let object = cleanup_type_description(&object)
.map_err(|e| anyhow::anyhow!("Failed to cleanup type description for type `{}`: {}", name, e))?;
let data = json!(schemars::schema::Schema::Object(object));
let mut output = hbs.render("type", &data)?;
@ -576,6 +602,39 @@ fn generate_type(
Ok(())
}
fn cleanup_type_description(object: &schemars::schema::SchemaObject) -> Result<schemars::schema::SchemaObject> {
let mut object = object.clone();
if let Some(metadata) = object.metadata.as_mut() {
if let Some(description) = metadata.description.as_mut() {
// Find any ```kcl code blocks and format the code.
// Parse any code blocks from the doc string.
let mut code_blocks = Vec::new();
let d = description.clone();
for line in d.lines() {
if line.starts_with("```kcl") && line.ends_with("```") {
code_blocks.push(line);
}
}
// Parse the kcl and recast it.
for code_block in &code_blocks {
let trimmed = code_block.trim_start_matches("```kcl").trim_end_matches("```");
let program = crate::Program::parse_no_errs(trimmed)?;
let options = crate::parsing::ast::types::FormatOptions {
insert_final_newline: false,
..Default::default()
};
let cleaned = program.ast.recast(&options, 0);
*description = description.replace(code_block, &format!("```kcl\n{}\n```", cleaned));
}
}
}
Ok(object)
}
fn clean_function_name(name: &str) -> String {
// Convert from camel case to snake case.
let mut fn_name = name.to_case(convert_case::Case::Snake);
@ -733,6 +792,9 @@ fn recurse_and_create_references(
}
}
let obj = cleanup_type_description(&obj)
.map_err(|e| anyhow::anyhow!("Failed to cleanup type description for type `{}`: {}", name, e))?;
Ok(schemars::schema::Schema::Object(obj.clone()))
}

View File

@ -31,14 +31,14 @@ layout: manual
| Name | Type | Description | Required |
|----------|------|-------------|----------|
{{#each args}}
| `{{name}}` | `{{type_}}` | {{{description}}} | {{#if required}}Yes{{else}}No{{/if}} |
| `{{name}}` | `{{type_}}` | {{{firstLine description}}} | {{#if required}}Yes{{else}}No{{/if}} |
{{/each}}
{{/if}}
{{#if return_value}}
### Returns
`{{return_value.type_}}` {{#if return_value.description}}- {{{return_value.description}}}{{/if}}
`{{return_value.type_}}` {{#if return_value.description}}- {{{firstLine return_value.description}}}{{/if}}
{{/if}}

View File

@ -8,6 +8,6 @@
| Property | Type | Description | Required |
|----------|------|-------------|----------|
{{#each properties}}
| `{{@key}}` | {{~ > propertyType this ~}} | {{{this.description}}} | {{#if (lookup ../required @key)}}Yes{{else}}No{{/if}} |
| `{{@key}}` | {{~ > propertyType this ~}} | {{{firstLine this.description}}} | {{#if (lookup ../required @key)}}Yes{{else}}No{{/if}} |
{{/each}}
{{/if}}

View File

@ -408,6 +408,46 @@ pub enum PlaneType {
}
/// A sketch is a collection of paths.
///
/// When you define a sketch to a variable like:
///
/// ```kcl
/// mySketch = startSketchOn('XY')
/// |> startProfileAt([-12, 12], %)
/// |> line(end = [24, 0])
/// |> line(end = [0, -24])
/// |> line(end = [-24, 0])
/// |> close()
/// ```
///
/// The `mySketch` variable will be an executed `Sketch` object. Executed being past
/// tense, because the engine has already executed the commands to create the sketch.
///
/// The previous sketch commands will never be executed again, in this case.
///
/// If you would like to encapsulate the commands to create the sketch any time you call it,
/// you can use a function.
///
/// ```kcl
/// fn createSketch() {
/// return startSketchOn('XY')
/// |> startProfileAt([-12, 12], %)
/// |> line(end = [24, 0])
/// |> line(end = [0, -24])
/// |> line(end = [-24, 0])
/// |> close()
/// }
/// ```
///
/// Now, every time you call `createSketch()`, the commands will be
/// executed and a new sketch will be created.
///
/// When you assign the result of `createSketch()` to a variable (`mySketch = createSketch()`), you are assigning
/// the executed sketch to that variable. Meaning that the sketch `mySketch` will not be executed
/// again.
///
/// You can still execute _new_ commands on the sketch like `extrude`, `revolve`, `loft`, etc. and
/// the sketch will be updated.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
@ -538,7 +578,49 @@ impl Sketch {
}
}
/// An solid is a collection of extrude surfaces.
/// A solid is a collection of extrude surfaces.
///
/// When you define a solid to a variable like:
///
/// ```kcl
/// myPart = startSketchOn('XY')
/// |> startProfileAt([-12, 12], %)
/// |> line(end = [24, 0])
/// |> line(end = [0, -24])
/// |> line(end = [-24, 0])
/// |> close()
/// |> extrude(length = 6)
/// ```
///
/// The `myPart` variable will be an executed `Solid` object. Executed being past
/// tense, because the engine has already executed the commands to create the solid.
///
/// The previous solid commands will never be executed again, in this case.
///
/// If you would like to encapsulate the commands to create the solid any time you call it,
/// you can use a function.
///
/// ```kcl
/// fn createPart() {
/// return startSketchOn('XY')
/// |> startProfileAt([-12, 12], %)
/// |> line(end = [24, 0])
/// |> line(end = [0, -24])
/// |> line(end = [-24, 0])
/// |> close()
/// |> extrude(length = 6)
/// }
/// ```
///
/// Now, every time you call `createPart()`, the commands will be
/// executed and a new solid will be created.
///
/// When you assign the result of `createPart()` to a variable (`myPart = createPart()`), you are assigning
/// the executed solid to that variable. Meaning that the solid `myPart` will not be executed
/// again.
///
/// You can still execute _new_ commands on the solid like `shell`, `fillet`, `chamfer`, etc.
/// and the solid will be updated.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]

View File

@ -730,6 +730,7 @@ pub enum UnitType {
}
// TODO called UnitLen so as not to clash with UnitLength in settings)
/// A unit of length.
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
#[ts(export)]
#[serde(tag = "type")]
@ -811,6 +812,7 @@ impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
}
}
/// A unit of angle.
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
#[ts(export)]
#[serde(tag = "type")]