Some improvements to the boxed signatures in the docs (#6593)
* Show a more reasonable name in function docs Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fix buggy docs for union types Signed-off-by: Nick Cameron <nrc@ncameron.org> * Make types in the docs signatures into links Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -461,7 +461,7 @@ fn generate_type_from_kcl(ty: &TyData, file_name: String, example_name: String)
|
||||
|
||||
let data = json!({
|
||||
"name": ty.qual_name(),
|
||||
"definition": ty.alias.as_ref().map(|t| format!("type {} = {t}", ty.name)),
|
||||
"definition": ty.alias.as_ref().map(|t| cleanup_sig(format!("type {} = {t}", ty.preferred_name), ty.referenced_types.iter().filter(|t| !DECLARED_TYPES.contains(&&***t)))),
|
||||
"summary": ty.summary,
|
||||
"description": ty.description,
|
||||
"deprecated": ty.properties.deprecated,
|
||||
@ -485,8 +485,6 @@ fn generate_function_from_kcl(function: &FnData, file_name: String) -> Result<()
|
||||
|
||||
let hbs = init_handlebars()?;
|
||||
|
||||
let name = function.name.clone();
|
||||
|
||||
let examples: Vec<serde_json::Value> = function
|
||||
.examples
|
||||
.iter()
|
||||
@ -499,7 +497,10 @@ fn generate_function_from_kcl(function: &FnData, file_name: String) -> Result<()
|
||||
"summary": function.summary,
|
||||
"description": function.description,
|
||||
"deprecated": function.properties.deprecated,
|
||||
"fn_signature": name.clone() + &function.fn_signature(),
|
||||
"fn_signature": cleanup_sig(function.preferred_name.clone() + &function.fn_signature(), function
|
||||
.referenced_types
|
||||
.iter()
|
||||
.filter(|t| !DECLARED_TYPES.contains(&&***t))),
|
||||
"tags": [],
|
||||
"examples": examples,
|
||||
"is_utilities": false,
|
||||
@ -624,7 +625,7 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<BTreeMap<String,
|
||||
"summary": internal_fn.summary(),
|
||||
"description": internal_fn.description(),
|
||||
"deprecated": internal_fn.deprecated(),
|
||||
"fn_signature": internal_fn.fn_signature(true),
|
||||
"fn_signature": cleanup_sig(internal_fn.fn_signature(true), types.keys()),
|
||||
"tags": internal_fn.tags(),
|
||||
"examples": examples,
|
||||
"is_utilities": internal_fn.tags().contains(&"utilities".to_string()),
|
||||
@ -653,20 +654,6 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<BTreeMap<String,
|
||||
Ok(types)
|
||||
}
|
||||
|
||||
fn cleanup_static_links(output: &str) -> String {
|
||||
let mut cleaned_output = output.to_string();
|
||||
// Fix the links to the types.
|
||||
// Gross hack for the stupid alias types.
|
||||
cleaned_output = cleaned_output.replace("TagNode", "TagDeclarator");
|
||||
|
||||
let link = format!("[`{}`](/docs/kcl/types#tag-declaration)", "TagDeclarator");
|
||||
cleaned_output = cleaned_output.replace("`TagDeclarator`", &link);
|
||||
let link = format!("[`{}`](/docs/kcl/types#tag-identifier)", "TagIdentifier");
|
||||
cleaned_output = cleaned_output.replace("`TagIdentifier`", &link);
|
||||
|
||||
cleaned_output
|
||||
}
|
||||
|
||||
// Fix the links to the types.
|
||||
fn cleanup_type_links<'a>(output: &str, types: impl Iterator<Item = &'a String>) -> String {
|
||||
let mut cleaned_output = output.to_string();
|
||||
@ -675,11 +662,11 @@ fn cleanup_type_links<'a>(output: &str, types: impl Iterator<Item = &'a String>)
|
||||
// TODO: This is a hack for the handlebars template being too complex.
|
||||
cleaned_output = cleaned_output.replace("`[, `number`, `number`]`", "`[number, number]`");
|
||||
cleaned_output = cleaned_output.replace("`[, `number`, `number`, `number`]`", "`[number, number, number]`");
|
||||
cleaned_output = cleaned_output.replace("`[, `integer`, `integer`, `integer`]`", "`[integer, integer, integer]`");
|
||||
|
||||
// Fix the links to the types.
|
||||
for type_name in types.map(|s| &**s).chain(DECLARED_TYPES) {
|
||||
if type_name == "TagDeclarator" || type_name == "TagIdentifier" || type_name == "TagNode" {
|
||||
if type_name == "TagDeclarator" || type_name == "TagIdentifier" || type_name == "TagNode" || type_name == "tag"
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
let link = format!("(/docs/kcl/types/{})", type_name);
|
||||
@ -694,10 +681,70 @@ fn cleanup_type_links<'a>(output: &str, types: impl Iterator<Item = &'a String>)
|
||||
// TODO handle union types generically rather than special casing them.
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"`Sketch | Plane | Face`",
|
||||
"[`Sketch`](/docs/kcl/types/Sketch) OR [`Plane`](/docs/kcl/types/Plane) OR [`Face`](/docs/kcl/types/Face)",
|
||||
"[`Sketch`](/docs/kcl/types/Sketch) or [`Plane`](/docs/kcl/types/Plane) or [`Face`](/docs/kcl/types/Face)",
|
||||
);
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"`Axis3d | Edge`",
|
||||
"[`Axis3d`](/docs/kcl/types/Axis3d) or [`Edge`](/docs/kcl/types/Edge)",
|
||||
);
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"`Axis2d | Edge`",
|
||||
"[`Axis2d`](/docs/kcl/types/Axis2d) or [`Edge`](/docs/kcl/types/Edge)",
|
||||
);
|
||||
|
||||
cleanup_static_links(&cleaned_output)
|
||||
// Fix the links to the types.
|
||||
// Gross hack for the stupid alias types.
|
||||
cleaned_output = cleaned_output.replace("TagNode", "TagDeclarator");
|
||||
|
||||
let link = "[`TagDeclarator`](/docs/kcl/types#tag-declaration)";
|
||||
cleaned_output = cleaned_output.replace("`TagDeclarator`", link);
|
||||
let link = "[`TagIdentifier`](/docs/kcl/types#tag-identifier)";
|
||||
cleaned_output = cleaned_output.replace("`TagIdentifier`", link);
|
||||
|
||||
cleaned_output
|
||||
}
|
||||
|
||||
// TODO total code dup with `cleanup_type_links`, just this version doesn't have the backticks. :-(
|
||||
fn cleanup_sig<'a>(input: String, types: impl Iterator<Item = &'a String>) -> String {
|
||||
let mut cleaned_output = input;
|
||||
|
||||
// Fix the links to the types.
|
||||
for type_name in types.map(|s| &**s).chain(DECLARED_TYPES) {
|
||||
if type_name == "TagDeclarator" || type_name == "TagIdentifier" || type_name == "TagNode" || type_name == "tag"
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
let link = format!("(/docs/kcl/types/{})", type_name);
|
||||
// Do the same for the type with brackets.
|
||||
cleaned_output = cleaned_output.replace(&format!("[{}]", type_name), &format!("[[{}]]{}", type_name, link));
|
||||
cleaned_output = cleaned_output.replace(type_name, &format!("[{}]{}", type_name, &link));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle union types generically rather than special casing them.
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"Sketch | Plane | Face",
|
||||
"[Sketch](/docs/kcl/types/Sketch) | [Plane](/docs/kcl/types/Plane) | [Face](/docs/kcl/types/Face)",
|
||||
);
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"Axis3d | Edge",
|
||||
"[Axis3d](/docs/kcl/types/Axis3d) | [Edge](/docs/kcl/types/Edge)",
|
||||
);
|
||||
cleaned_output = cleaned_output.replace(
|
||||
"Axis2d | Edge",
|
||||
"[Axis2d](/docs/kcl/types/Axis2d) | [Edge](/docs/kcl/types/Edge)",
|
||||
);
|
||||
|
||||
// Fix the links to the types.
|
||||
// Gross hack for the stupid alias types.
|
||||
cleaned_output = cleaned_output.replace("TagNode", "TagDeclarator");
|
||||
|
||||
let link = "[TagDeclarator](/docs/kcl/types#tag-declaration)";
|
||||
cleaned_output = cleaned_output.replace("TagDeclarator", link);
|
||||
let link = "[TagIdentifier](/docs/kcl/types#tag-identifier)";
|
||||
cleaned_output = cleaned_output.replace("TagIdentifier", link);
|
||||
|
||||
cleaned_output
|
||||
}
|
||||
|
||||
fn add_to_types(
|
||||
|
@ -1,4 +1,8 @@
|
||||
use std::{collections::HashSet, fmt, str::FromStr};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
use tower_lsp::lsp_types::{
|
||||
@ -9,7 +13,7 @@ use tower_lsp::lsp_types::{
|
||||
use crate::{
|
||||
execution::annotations,
|
||||
parsing::{
|
||||
ast::types::{Annotation, ImportSelector, Node, PrimitiveType, Type, VariableKind},
|
||||
ast::types::{Annotation, ImportSelector, ItemVisibility, Node, PrimitiveType, Type, VariableKind},
|
||||
token::NumericSuffix,
|
||||
},
|
||||
ModuleId,
|
||||
@ -17,19 +21,41 @@ use crate::{
|
||||
|
||||
pub fn walk_prelude() -> Vec<DocData> {
|
||||
let mut visitor = CollectionVisitor::default();
|
||||
visitor.visit_module("prelude", "").unwrap();
|
||||
visitor.result
|
||||
visitor.visit_module("prelude", "", WalkForNames::All).unwrap();
|
||||
visitor.result.into_values().collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct CollectionVisitor {
|
||||
name: String,
|
||||
result: Vec<DocData>,
|
||||
result: HashMap<String, DocData>,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum WalkForNames<'a> {
|
||||
All,
|
||||
Selected(Vec<&'a str>),
|
||||
}
|
||||
|
||||
impl<'a> WalkForNames<'a> {
|
||||
fn contains(&self, name: &str) -> bool {
|
||||
match self {
|
||||
WalkForNames::All => true,
|
||||
WalkForNames::Selected(names) => names.contains(&name),
|
||||
}
|
||||
}
|
||||
|
||||
fn intersect(&self, names: impl Iterator<Item = &'a str>) -> Self {
|
||||
match self {
|
||||
WalkForNames::All => WalkForNames::Selected(names.collect()),
|
||||
WalkForNames::Selected(mine) => WalkForNames::Selected(names.filter(|n| mine.contains(n)).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionVisitor {
|
||||
fn visit_module(&mut self, name: &str, preferred_prefix: &str) -> Result<(), String> {
|
||||
fn visit_module(&mut self, name: &str, preferred_prefix: &str, names: WalkForNames) -> Result<(), String> {
|
||||
let old_name = std::mem::replace(&mut self.name, name.to_owned());
|
||||
let source = crate::modules::read_std(name).unwrap();
|
||||
let parsed = crate::parsing::parse_str(source, ModuleId::from_usize(self.id))
|
||||
@ -38,23 +64,29 @@ impl CollectionVisitor {
|
||||
self.id += 1;
|
||||
|
||||
for n in &parsed.body {
|
||||
if n.visibility() != ItemVisibility::Export {
|
||||
continue;
|
||||
}
|
||||
match n {
|
||||
crate::parsing::ast::types::BodyItem::ImportStatement(import) if !import.visibility.is_default() => {
|
||||
match &import.path {
|
||||
crate::parsing::ast::types::ImportPath::Std { path } => {
|
||||
match import.selector {
|
||||
ImportSelector::Glob(..) => self.visit_module(&path[1], "")?,
|
||||
ImportSelector::None { .. } => {
|
||||
self.visit_module(&path[1], &format!("{}::", import.module_name().unwrap()))?
|
||||
}
|
||||
// Only supports glob or whole-module imports for now (make sure the module is re-exported as well as some of the names in it).
|
||||
_ => {}
|
||||
crate::parsing::ast::types::BodyItem::ImportStatement(import) => match &import.path {
|
||||
crate::parsing::ast::types::ImportPath::Std { path } => match &import.selector {
|
||||
ImportSelector::Glob(..) => self.visit_module(&path[1], "", names.clone())?,
|
||||
ImportSelector::None { .. } => {
|
||||
let name = import.module_name().unwrap();
|
||||
if names.contains(&name) {
|
||||
self.visit_module(&path[1], &format!("{}::", name), WalkForNames::All)?;
|
||||
}
|
||||
}
|
||||
p => return Err(format!("Unexpected import: `{p}`")),
|
||||
ImportSelector::List { items } => {
|
||||
self.visit_module(&path[1], "", names.intersect(items.iter().map(|n| &*n.name.name)))?
|
||||
}
|
||||
},
|
||||
p => return Err(format!("Unexpected import: `{p}`")),
|
||||
},
|
||||
crate::parsing::ast::types::BodyItem::VariableDeclaration(var) => {
|
||||
if !names.contains(var.name()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
crate::parsing::ast::types::BodyItem::VariableDeclaration(var) if !var.visibility.is_default() => {
|
||||
let qual_name = if self.name == "prelude" {
|
||||
"std::".to_owned()
|
||||
} else {
|
||||
@ -66,6 +98,10 @@ impl CollectionVisitor {
|
||||
DocData::Const(ConstData::from_ast(var, qual_name, preferred_prefix, name))
|
||||
}
|
||||
};
|
||||
let key = format!("I:{}", dd.qual_name());
|
||||
if self.result.contains_key(&key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dd.with_meta(&var.outer_attrs);
|
||||
for a in &var.outer_attrs {
|
||||
@ -73,15 +109,22 @@ impl CollectionVisitor {
|
||||
}
|
||||
dd.with_comments(n.get_comments());
|
||||
|
||||
self.result.push(dd);
|
||||
self.result.insert(key, dd);
|
||||
}
|
||||
crate::parsing::ast::types::BodyItem::TypeDeclaration(ty) if !ty.visibility.is_default() => {
|
||||
crate::parsing::ast::types::BodyItem::TypeDeclaration(ty) => {
|
||||
if !names.contains(ty.name()) {
|
||||
continue;
|
||||
}
|
||||
let qual_name = if self.name == "prelude" {
|
||||
"std::".to_owned()
|
||||
} else {
|
||||
format!("std::{}::", self.name)
|
||||
};
|
||||
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name, preferred_prefix, name));
|
||||
let key = format!("T:{}", dd.qual_name());
|
||||
if self.result.contains_key(&key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dd.with_meta(&ty.outer_attrs);
|
||||
for a in &ty.outer_attrs {
|
||||
@ -89,7 +132,7 @@ impl CollectionVisitor {
|
||||
}
|
||||
dd.with_comments(n.get_comments());
|
||||
|
||||
self.result.push(dd);
|
||||
self.result.insert(key, dd);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -116,6 +159,14 @@ impl DocData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn qual_name(&self) -> &str {
|
||||
match self {
|
||||
DocData::Fn(f) => &f.qual_name,
|
||||
DocData::Const(c) => &c.qual_name,
|
||||
DocData::Ty(t) => &t.qual_name,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of the module in which the item is declared, e.g., `sketch`
|
||||
#[allow(dead_code)]
|
||||
pub fn module_name(&self) -> &str {
|
||||
|
4
rust/kcl-lib/src/docs/templates/const.hbs
vendored
4
rust/kcl-lib/src/docs/templates/const.hbs
vendored
@ -12,7 +12,7 @@ layout: manual
|
||||
|
||||
{{{description}}}
|
||||
|
||||
```js
|
||||
```kcl
|
||||
{{{name}}}{{#if type_}}: {{{type_}}}{{/if}}{{#if value}} = {{{value}}}{{/if}}
|
||||
```
|
||||
|
||||
@ -20,7 +20,7 @@ layout: manual
|
||||
### Examples
|
||||
|
||||
{{#each examples}}
|
||||
```js
|
||||
```kcl
|
||||
{{{this.content}}}
|
||||
```
|
||||
|
||||
|
4
rust/kcl-lib/src/docs/templates/function.hbs
vendored
4
rust/kcl-lib/src/docs/templates/function.hbs
vendored
@ -12,7 +12,7 @@ layout: manual
|
||||
|
||||
{{{description}}}
|
||||
|
||||
```js
|
||||
```kcl
|
||||
{{{fn_signature}}}
|
||||
```
|
||||
|
||||
@ -47,7 +47,7 @@ layout: manual
|
||||
|
||||
{{#each examples}}
|
||||
{{#if this.content}}
|
||||
```js
|
||||
```kcl
|
||||
{{{this.content}}}
|
||||
```
|
||||
{{/if}}
|
||||
|
2
rust/kcl-lib/src/docs/templates/kclType.hbs
vendored
2
rust/kcl-lib/src/docs/templates/kclType.hbs
vendored
@ -24,7 +24,7 @@ layout: manual
|
||||
|
||||
{{#each examples}}
|
||||
{{#if this.content}}
|
||||
```js
|
||||
```kcl
|
||||
{{{this.content}}}
|
||||
```
|
||||
{{/if}}
|
||||
|
Reference in New Issue
Block a user