Misc docs polishing (#6712)

* Fake modules for Rust std lib functions

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Include the missing @ in Rust std lib fns

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Move revolve and mirror2d to better modules

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Use docs from KCL mods for type summaries

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Use type docs to describe types from KCL std lib

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-06 16:09:59 +12:00
committed by GitHub
parent 1e056cfd8a
commit 1841e63021
116 changed files with 1178 additions and 1095 deletions

View File

@ -9,13 +9,11 @@ use serde_json::json;
use tokio::task::JoinSet;
use super::kcl_doc::{ConstData, DocData, ExampleProperties, FnData, ModData, TyData};
use crate::{docs::StdLibFn, std::StdLib, ExecutorContext};
// These types are declared in (KCL) std.
const DECLARED_TYPES: [&str; 15] = [
"any", "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Edge", "Point2d",
"Point3d", "Axis2d", "Axis3d",
];
use crate::{
docs::{StdLibFn, DECLARED_TYPES},
std::StdLib,
ExecutorContext,
};
// Types with special handling.
const SPECIAL_TYPES: [&str; 5] = ["TagDeclarator", "TagIdentifier", "Start", "End", "ImportedGeometry"];
@ -126,9 +124,12 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &ModD
continue;
}
let tags = internal_fn.tags();
let module = tags.first().map(|s| format!("std::{s}")).unwrap_or("std".to_owned());
functions
.get_mut("std")
.unwrap()
.entry(module.to_owned())
.or_default()
.push((internal_fn.name(), internal_fn.name()));
}
@ -319,7 +320,12 @@ fn generate_mod_from_kcl(m: &ModData, file_name: String) -> Result<()> {
Ok(())
}
fn generate_function_from_kcl(function: &FnData, file_name: String, example_name: String) -> Result<()> {
fn generate_function_from_kcl(
function: &FnData,
file_name: String,
example_name: String,
kcl_std: &ModData,
) -> Result<()> {
if function.properties.doc_hidden {
return Ok(());
}
@ -344,14 +350,14 @@ fn generate_function_from_kcl(function: &FnData, file_name: String, example_name
json!({
"name": arg.name,
"type_": arg.ty,
"description": arg.docs.as_deref().unwrap_or(""),
"description": arg.docs.clone().or_else(|| arg.ty.as_ref().and_then(|t| super::docs_for_type(t, kcl_std))).unwrap_or_default(),
"required": arg.kind.required(),
})
}).collect::<Vec<_>>(),
"return_value": function.return_type.as_ref().map(|t| {
json!({
"type_": t,
"description": "",
"description": super::docs_for_type(t, kcl_std).unwrap_or_default(),
})
}),
});
@ -392,7 +398,7 @@ fn generate_const_from_kcl(cnst: &ConstData, file_name: String, example_name: St
Ok(())
}
fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<()> {
fn generate_function(internal_fn: Box<dyn StdLibFn>, kcl_std: &ModData) -> Result<()> {
let hbs = init_handlebars()?;
if internal_fn.unpublished() {
@ -428,8 +434,11 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<()> {
})
.collect();
let tags = internal_fn.tags();
let qual = tags.first().map(|s| &**s).unwrap_or("");
let data = json!({
"name": fn_name,
"name": format!("std::{qual}{}{fn_name}", if qual.is_empty() { "" } else {"::"}),
"summary": internal_fn.summary(),
"description": internal_fn.description(),
"deprecated": internal_fn.deprecated(),
@ -439,14 +448,14 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<()> {
json!({
"name": arg.name,
"type_": rename_type(&arg.type_),
"description": arg.description(),
"description": arg.description(Some(kcl_std)),
"required": arg.required,
})
}).collect::<Vec<_>>(),
"return_value": internal_fn.return_value(false).map(|ret| {
json!({
"type_": rename_type(&ret.type_),
"description": ret.description(),
"description": ret.description(Some(kcl_std)),
})
}),
});
@ -651,12 +660,12 @@ fn test_generate_stdlib_markdown_docs() {
for key in combined.keys().sorted() {
let internal_fn = combined.get(key).unwrap();
generate_function(internal_fn.clone()).unwrap();
generate_function(internal_fn.clone(), &kcl_std).unwrap();
}
for d in kcl_std.all_docs() {
match d {
DocData::Fn(f) => generate_function_from_kcl(f, d.file_name(), d.example_name()).unwrap(),
DocData::Fn(f) => generate_function_from_kcl(f, d.file_name(), d.example_name(), &kcl_std).unwrap(),
DocData::Const(c) => generate_const_from_kcl(c, d.file_name(), d.example_name()).unwrap(),
DocData::Ty(t) => generate_type_from_kcl(t, d.file_name(), d.example_name()).unwrap(),
DocData::Mod(m) => generate_mod_from_kcl(m, d.file_name()).unwrap(),

View File

@ -307,6 +307,15 @@ impl DocData {
_ => unreachable!(),
}
}
pub(super) fn summary(&self) -> Option<&String> {
match self {
DocData::Fn(f) => f.summary.as_ref(),
DocData::Const(c) => c.summary.as_ref(),
DocData::Ty(t) => t.summary.as_ref(),
DocData::Mod(m) => m.summary.as_ref(),
}
}
}
#[derive(Debug, Clone)]
@ -459,7 +468,6 @@ impl ModData {
}
}
#[cfg(test)]
pub fn find_by_name(&self, name: &str) -> Option<&DocData> {
if let Some(result) = self.children.values().find(|dd| dd.name() == name) {
return Some(result);

View File

@ -10,6 +10,7 @@ use std::{
};
use anyhow::Result;
use kcl_doc::ModData;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::{
@ -22,6 +23,12 @@ use crate::{
std::Primitive,
};
// These types are declared in (KCL) std.
const DECLARED_TYPES: [&str; 15] = [
"any", "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Edge", "Point2d",
"Point3d", "Axis2d", "Axis3d",
];
lazy_static::lazy_static! {
static ref NUMERIC_TYPE_SCHEMA: schemars::schema::SchemaObject = {
let mut settings = schemars::gen::SchemaSettings::openapi3();
@ -94,6 +101,9 @@ pub struct StdLibFnArg {
impl fmt::Display for StdLibFnArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.label_required {
f.write_char('@')?;
}
f.write_str(&self.name)?;
if !self.required {
f.write_char('?')?;
@ -154,11 +164,18 @@ impl StdLibFnArg {
.map(|maybe| maybe.map(|(index, snippet)| (index, format!("{label}{snippet}"))))
}
pub fn description(&self) -> Option<String> {
pub fn description(&self, kcl_std: Option<&ModData>) -> Option<String> {
// Check if we explicitly gave this stdlib arg a description.
if !self.description.is_empty() {
return Some(self.description.clone());
}
if let Some(kcl_std) = kcl_std {
if let Some(t) = docs_for_type(&self.type_, kcl_std) {
return Some(t);
}
}
// If not, then try to get something meaningful from the schema.
get_description_string_from_schema(&self.schema.clone())
}
@ -370,7 +387,7 @@ impl From<StdLibFnArg> for ParameterInformation {
fn from(arg: StdLibFnArg) -> Self {
ParameterInformation {
label: ParameterLabel::Simple(arg.name.to_string()),
documentation: arg.description().map(|description| {
documentation: arg.description(None).map(|description| {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: description,
@ -380,6 +397,16 @@ impl From<StdLibFnArg> for ParameterInformation {
}
}
fn docs_for_type(ty: &str, kcl_std: &ModData) -> Option<String> {
if DECLARED_TYPES.contains(&ty) {
if let Some(data) = kcl_std.find_by_name(ty) {
return data.summary().cloned();
}
}
None
}
/// This trait defines functions called upon stdlib functions to generate
/// documentation for them.
pub trait StdLibFn: std::fmt::Debug + Send + Sync {
@ -1128,7 +1155,7 @@ mod tests {
assert_eq!(
sh.signatures[0].label,
r#"extrude(
sketches: [Sketch],
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,

View File

@ -1116,7 +1116,7 @@ async fn test_kcl_lsp_signature_help() {
assert_eq!(
signature_help.signatures[0].label,
r#"startSketchOn(
planeOrSolid: SketchData,
@planeOrSolid: SketchData,
face?: FaceTag,
): SketchSurface"#
);
@ -1205,7 +1205,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
sketches: [Sketch],
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
@ -1303,7 +1303,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
sketches: [Sketch],
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
@ -1396,7 +1396,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
sketches: [Sketch],
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
@ -1494,7 +1494,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
sketches: [Sketch],
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,

View File

@ -95,6 +95,8 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
"types" => Some(include_str!("../std/types.kcl")),
"solid" => Some(include_str!("../std/solid.kcl")),
"units" => Some(include_str!("../std/units.kcl")),
"array" => Some(include_str!("../std/array.kcl")),
"transform" => Some(include_str!("../std/transform.kcl")),
_ => None,
}
}

View File

@ -236,13 +236,13 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::helix::helix(e, a)),
StdFnProps::default("std::helix").include_in_feature_tree(),
),
("sketch", "mirror2d") => (
("transform", "mirror2d") => (
|e, a| Box::pin(crate::std::mirror::mirror_2d(e, a)),
StdFnProps::default("std::sketch::mirror2d"),
StdFnProps::default("std::transform::mirror2d"),
),
("prelude", "revolve") => (
("sketch", "revolve") => (
|e, a| Box::pin(crate::std::revolve::revolve(e, a)),
StdFnProps::default("std::revolve").include_in_feature_tree(),
StdFnProps::default("std::sketch::revolve").include_in_feature_tree(),
),
("prelude", "offsetPlane") => (
|e, a| Box::pin(crate::std::planes::offset_plane(e, a)),