summaryrefslogtreecommitdiff
path: root/macros/src/func.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-17 16:24:29 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-17 16:24:29 +0100
commit35b16e545b4fce299edbc00c9a9754179fa51634 (patch)
treeeb1081e55187e59ff6482abc1ac2f1932606ef59 /macros/src/func.rs
parentb6202b646a0d5ecced301d9bac8bfcaf977d7ee4 (diff)
Document parameters in comment
Diffstat (limited to 'macros/src/func.rs')
-rw-r--r--macros/src/func.rs122
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(&section);
+ 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)
+}