diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-17 16:24:29 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-17 16:24:29 +0100 |
| commit | 35b16e545b4fce299edbc00c9a9754179fa51634 (patch) | |
| tree | eb1081e55187e59ff6482abc1ac2f1932606ef59 /macros/src/func.rs | |
| parent | b6202b646a0d5ecced301d9bac8bfcaf977d7ee4 (diff) | |
Document parameters in comment
Diffstat (limited to 'macros/src/func.rs')
| -rw-r--r-- | macros/src/func.rs | 122 |
1 files changed, 101 insertions, 21 deletions
diff --git a/macros/src/func.rs b/macros/src/func.rs index 4523d48a..98fca6a4 100644 --- a/macros/src/func.rs +++ b/macros/src/func.rs @@ -1,36 +1,26 @@ +use proc_macro2::Span; +use unscanny::Scanner; + use super::*; /// Expand the `#[func]` macro. pub fn func(item: syn::Item) -> Result<TokenStream> { - let doc_comment = match &item { - syn::Item::Struct(item) => doc_comment(&item.attrs), - syn::Item::Enum(item) => doc_comment(&item.attrs), - syn::Item::Fn(item) => doc_comment(&item.attrs), + let mut docs = match &item { + syn::Item::Struct(item) => documentation(&item.attrs), + syn::Item::Enum(item) => documentation(&item.attrs), + syn::Item::Fn(item) => documentation(&item.attrs), _ => String::new(), }; - let mut tags = vec![]; - let mut kept = vec![]; - for line in doc_comment.lines() { - let line = line.trim(); - if let Some(suffix) = line.trim_end_matches(".").strip_prefix("Tags: ") { - tags.extend(suffix.split(", ")); - } else { - kept.push(line); - } - } - - while kept.last().map_or(false, |line| line.is_empty()) { - kept.pop(); - } - - let docs = kept.join("\n"); + let tags = tags(&mut docs); + let params = params(&mut docs)?; + let docs = docs.trim(); let info = quote! { ::typst::model::FuncInfo { name, docs: #docs, tags: &[#(#tags),*], - params: ::std::vec![], + params: ::std::vec![#(#params),*], } }; @@ -83,3 +73,93 @@ pub fn func(item: syn::Item) -> Result<TokenStream> { }) } } + +/// Extract a section. +pub fn section(docs: &mut String, title: &str) -> Option<String> { + let needle = format!("# {title}\n"); + let start = docs.find(&needle)?; + let rest = &docs[start..]; + let len = rest[1..].find('#').map(|x| 1 + x).unwrap_or(rest.len()); + let end = start + len; + let section = docs[start + needle.len()..].to_owned(); + docs.replace_range(start..end, ""); + Some(section) +} + +/// Parse the tag section. +pub fn tags(docs: &mut String) -> Vec<String> { + section(docs, "Tags") + .unwrap_or_default() + .lines() + .filter_map(|line| line.strip_prefix('-')) + .map(|s| s.trim().into()) + .collect() +} + +/// Parse the parameter section. +pub fn params(docs: &mut String) -> Result<Vec<TokenStream>> { + let Some(section) = section(docs, "Parameters") else { return Ok(vec![]) }; + let mut s = Scanner::new(§ion); + let mut infos = vec![]; + + while s.eat_if('-') { + s.eat_whitespace(); + let name = s.eat_until(':'); + s.expect(": "); + let ty: syn::Type = syn::parse_str(s.eat_until(char::is_whitespace))?; + s.eat_whitespace(); + let mut named = false; + let mut positional = false; + let mut required = false; + let mut variadic = false; + let mut settable = false; + s.expect('('); + for part in s.eat_until(')').split(',').map(str::trim).filter(|s| !s.is_empty()) { + match part { + "named" => named = true, + "positional" => positional = true, + "required" => required = true, + "variadic" => variadic = true, + "settable" => settable = true, + _ => { + return Err(syn::Error::new( + Span::call_site(), + format!("unknown parameter flag {:?}", part), + )) + } + } + } + + if (!named && !positional) + || (variadic && !positional) + || (named && variadic) + || (required && variadic) + { + return Err(syn::Error::new( + Span::call_site(), + "invalid combination of parameter flags", + )); + } + + s.expect(')'); + let docs = dedent(s.eat_until("\n-").trim()); + infos.push(quote! { + ::typst::model::ParamInfo { + name: #name, + docs: #docs, + cast: <#ty as ::typst::model::Cast< + ::typst::syntax::Spanned<::typst::model::Value> + >>::describe(), + named: #named, + positional: #positional, + required: #required, + variadic: #variadic, + settable: #settable, + } + }); + + s.eat_whitespace(); + } + + Ok(infos) +} |
