Doc comments on parameters

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-03-22 10:38:18 +13:00
parent 0913d3ebdc
commit e0e707ad85
5 changed files with 91 additions and 19 deletions

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ use tower_lsp::lsp_types::{
use crate::{
execution::annotations,
parsing::{
ast::types::{Annotation, Node, NonCodeNode, PrimitiveType, Type, VariableKind},
ast::types::{Annotation, Node, PrimitiveType, Type, VariableKind},
token::NumericSuffix,
},
ModuleId,
@ -59,7 +59,6 @@ impl CollectionVisitor {
format!("std::{}::", self.name)
};
let mut dd = match var.kind {
// TODO metadata for args
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)),
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
};
@ -494,7 +493,7 @@ pub struct ArgData {
/// If the argument is required.
pub kind: ArgKind,
/// 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
/// This is helpful if the type is really basic, like "number" -- that won't tell the user much about
/// how this argument is meant to be used.
pub docs: Option<String>,
}
@ -509,21 +508,19 @@ pub enum ArgKind {
impl ArgData {
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
ArgData {
let mut result = ArgData {
name: arg.identifier.name.clone(),
ty: arg.type_.as_ref().map(|t| t.to_string()),
// Doc comments are not yet supported on parameters.
docs: None,
kind: if arg.labeled {
ArgKind::Labelled(arg.optional())
} else {
ArgKind::Special
},
}
}
};
fn _with_meta(&mut self, _meta: &[Node<NonCodeNode>]) {
// TODO use comments for docs (we can't currently get the comments for an argument)
result.with_comments(&arg.identifier.pre_comments);
result
}
pub fn get_autocomplete_snippet(&self, index: usize) -> Option<(usize, String)> {
@ -885,6 +882,37 @@ impl ApplyMeta for TyData {
}
}
impl ApplyMeta for ArgData {
fn apply_docs(
&mut self,
summary: Option<String>,
description: Option<String>,
_examples: Vec<(String, ExampleProperties)>,
) {
let Some(mut docs) = summary else {
return;
};
if let Some(desc) = description {
docs.push_str("\n\n");
docs.push_str(&desc);
}
self.docs = Some(docs);
}
fn deprecated(&mut self, _deprecated: bool) {
unreachable!();
}
fn doc_hidden(&mut self, _doc_hidden: bool) {
unreachable!();
}
fn impl_kind(&mut self, _impl_kind: annotations::Impl) {
unreachable!();
}
}
fn collect_type_names(acc: &mut HashSet<String>, ty: &Type) {
match ty {
Type::Primitive(primitive_type) => {

View File

@ -2809,15 +2809,40 @@ fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
any.try_map(|t: Token| t.value.parse()).parse_next(i)
}
fn comment(i: &mut TokenSlice) -> PResult<Node<String>> {
any.verify_map(|token: Token| {
let value = match token.token_type {
TokenType::LineComment => token.value,
TokenType::BlockComment => token.value,
_ => return None,
};
Some(Node::new(value, token.start, token.end, token.module_id))
})
.context(expected("Comment"))
.parse_next(i)
}
fn comments(i: &mut TokenSlice) -> PResult<Node<Vec<String>>> {
let comments: Vec<Node<String>> = repeat(1.., (comment, opt(whitespace)).map(|(c, _)| c)).parse_next(i)?;
let start = comments[0].start;
let module_id = comments[0].module_id;
let end = comments.last().unwrap().end;
let inner = comments.into_iter().map(|n| n.inner).collect();
Ok(Node::new(inner, start, end, module_id))
}
struct ParamDescription {
labeled: bool,
arg_name: Token,
type_: std::option::Option<Node<Type>>,
default_value: Option<DefaultParamVal>,
comments: Option<Node<Vec<String>>>,
}
fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
let (found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = (
let (_, comments, found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = (
opt(whitespace),
opt(comments),
opt(at_sign),
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
opt(question_mark),
@ -2827,6 +2852,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
opt((equals, opt(whitespace), literal).map(|(_, _, literal)| literal)),
)
.parse_next(i)?;
Ok(ParamDescription {
labeled: found_at_sign.is_none(),
arg_name,
@ -2841,6 +2867,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
return Err(ErrMode::Backtrack(ContextError::from(e)));
}
},
comments,
})
}
@ -2850,6 +2877,7 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
let candidates: Vec<_> = separated(0.., parameter, comma_sep)
.context(expected("function parameters"))
.parse_next(i)?;
opt(comma_sep).parse_next(i)?;
// Make sure all those tokens are valid parameters.
let params: Vec<Parameter> = candidates
@ -2860,8 +2888,13 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
arg_name,
type_,
default_value,
comments,
}| {
let identifier = Node::<Identifier>::try_from(arg_name)?;
let mut identifier = Node::<Identifier>::try_from(arg_name)?;
if let Some(comments) = comments {
identifier.comment_start = comments.start;
identifier.pre_comments = comments.inner;
}
Ok(Parameter {
identifier,

View File

@ -52,7 +52,7 @@ expression: actual
"commentStart": 8,
"end": 9,
"name": "x",
"start": 8,
"start": 7,
"type": "Identifier"
},
"labeled": false

View File

@ -22,4 +22,13 @@
/// example = extrude(exampleSketch, length = 5)
/// ```
@(impl = std_rust)
export fn circle(@sketch_or_surface: Sketch | Plane | Face, center: Point2d, radius: number, tag?: tag): Sketch {}
export fn circle(
/// Sketch to extend, or plane or surface to sketch on.
@sketch_or_surface: Sketch | Plane | Face,
/// The center of the circle.
center: Point2d,
/// The radius of the circle.
radius: number,
/// Create a new tag which refers to this circle.
tag?: tag,
): Sketch {}