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:
@ -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(),
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
|
Reference in New Issue
Block a user