diff --git a/codegen/src/attrs.rs b/codegen/src/attrs.rs index ede7c88b..0997430b 100644 --- a/codegen/src/attrs.rs +++ b/codegen/src/attrs.rs @@ -142,27 +142,31 @@ pub fn inner_item_attributes( } #[cfg(feature = "metadata")] -pub fn doc_attribute(attrs: &mut Vec) -> syn::Result { +pub fn doc_attributes(attrs: &mut Vec) -> syn::Result> { // Find the #[doc] attribute which will turn be read for function documentation. - let mut comments = String::new(); + let mut comments = Vec::new(); while let Some(index) = attrs .iter() .position(|attr| attr.path.get_ident().map(|i| *i == "doc").unwrap_or(false)) { - let attr = attrs.remove(index); - let meta = attr.parse_meta()?; - - match meta { + match attrs.remove(index).parse_meta()? { syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - if !comments.is_empty() { - comments += "\n"; + let mut line = s.value(); + + if line.contains('\n') { + // Must be a block comment `/** ... */` + line.insert_str(0, "/**"); + line.push_str("*/"); + } else { + // Single line - assume it is `///` + line.insert_str(0, "///"); } - comments += &s.value(); + comments.push(line); } _ => continue, } diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 53d1f95d..29e2a2cf 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -283,7 +283,7 @@ pub struct ExportedFn { params: ExportedFnParams, cfg_attrs: Vec, #[cfg(feature = "metadata")] - comment: String, + comments: Vec, } impl Parse for ExportedFn { @@ -407,7 +407,7 @@ impl Parse for ExportedFn { params: Default::default(), cfg_attrs, #[cfg(feature = "metadata")] - comment: Default::default(), + comments: Default::default(), }) } } @@ -508,13 +508,13 @@ impl ExportedFn { } #[cfg(feature = "metadata")] - pub fn comment(&self) -> &str { - &self.comment + pub fn comments(&self) -> &[String] { + &self.comments } #[cfg(feature = "metadata")] - pub fn set_comment(&mut self, comment: String) { - self.comment = comment + pub fn set_comments(&mut self, comments: Vec) { + self.comments = comments } pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec) { diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 38b2509f..14290c59 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -127,7 +127,7 @@ impl Parse for Module { f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs)); #[cfg(feature = "metadata")] - f.set_comment(crate::attrs::doc_attribute(&mut item_fn.attrs)?); + f.set_comments(crate::attrs::doc_attributes(&mut item_fn.attrs)?); Ok(f) })?; diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index bf12102c..01bacf62 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -166,14 +166,18 @@ pub fn generate_body( ); #[cfg(feature = "metadata")] - let (param_names, comment) = ( + let (param_names, comments) = ( quote! { Some(#fn_token_name::PARAM_NAMES) }, - function.comment(), + function + .comments() + .iter() + .map(|s| syn::LitStr::new(s, Span::call_site())) + .collect::>(), ); #[cfg(not(feature = "metadata"))] - let (param_names, comment) = (quote! { None }, ""); + let (param_names, comments) = (quote! { None }, Vec::::new()); - set_fn_statements.push(if comment.is_empty() { + set_fn_statements.push(if comments.is_empty() { syn::parse2::(quote! { #(#cfg_attrs)* m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, @@ -181,12 +185,10 @@ pub fn generate_body( }) .unwrap() } else { - let comment_literal = syn::LitStr::new(comment, Span::call_site()); - syn::parse2::(quote! { #(#cfg_attrs)* - m.set_fn_with_comment(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, - #param_names, &[#(#fn_input_types),*], #comment_literal, #fn_token_name().into()); + m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, + #param_names, &[#(#fn_input_types),*], &[#(#comments),*], #fn_token_name().into()); }) .unwrap() }); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 597dbb46..4ba006f7 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -38,7 +38,7 @@ mod module_tests { } #[test] - fn one_factory_fn_with_comment_module() { + fn one_factory_fn_with_comments_module() { let input_tokens: TokenStream = quote! { pub mod one_fn { /// This is a doc-comment. @@ -59,7 +59,20 @@ mod module_tests { assert!(item_mod.consts().is_empty()); assert_eq!(item_mod.fns().len(), 1); assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number"); - assert_eq!(item_mod.fns()[0].comment(), " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n "); + assert_eq!( + item_mod.fns()[0] + .comments() + .iter() + .cloned() + .collect::>(), + vec![ + "/// This is a doc-comment.", + "/// Another line.", + "/// block doc-comment ", + "/// Final line.", + "/** doc-comment\n in multiple lines\n */" + ] + ); assert_eq!(item_mod.fns()[0].arg_count(), 0); assert_eq!( item_mod.fns()[0].return_type().unwrap(), @@ -354,7 +367,7 @@ mod generate_tests { } #[test] - fn one_factory_fn_with_comment_module() { + fn one_factory_fn_with_comments_module() { let input_tokens: TokenStream = quote! { pub mod one_fn { /// This is a doc-comment. @@ -387,8 +400,8 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn_with_comment("get_mystic_number", FnNamespace::Internal, FnAccess::Public, - Some(get_mystic_number_token::PARAM_NAMES), &[], " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n ", + m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public, + Some(get_mystic_number_token::PARAM_NAMES), &[], &["/// This is a doc-comment.","/// Another line.","/// block doc-comment ","/// Final line.","/** doc-comment\n in multiple lines\n */"], get_mystic_number_token().into()); if flatten {} else {} } diff --git a/src/module/mod.rs b/src/module/mod.rs index c3dc4fc0..1c252317 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -55,7 +55,7 @@ pub struct FuncInfo { pub return_type_name: Identifier, /// Comments. #[cfg(feature = "metadata")] - pub comments: crate::SmartString, + pub comments: Option]>>, } impl FuncInfo { @@ -492,10 +492,7 @@ impl Module { #[cfg(feature = "metadata")] return_type_name: self.identifiers.get("Dynamic"), #[cfg(feature = "metadata")] - comments: fn_def - .comments - .as_ref() - .map_or(crate::SmartString::new_const(), |v| v.join("\n").into()), + comments: None, func: Into::::into(fn_def).into(), } .into(), @@ -624,7 +621,7 @@ impl Module { self.functions.contains_key(&hash_fn) } - /// Update the metadata (parameter names/types and return type) of a registered function. + /// _(metadata)_ Update the metadata (parameter names/types and return type) of a registered function. /// Exported under the `metadata` feature only. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. @@ -659,6 +656,47 @@ impl Module { self } + /// _(metadata)_ Update the metadata (parameter names/types, return type and doc-comments) of a + /// registered function. + /// Exported under the `metadata` feature only. + /// + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. + /// + /// ## Parameter Names and Types + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. In other words, + /// the number of entries should be one larger than the number of parameters. + /// + /// ## Comments + /// + /// Block doc-comments should be kept in a single line. + /// + /// Line doc-comments should be kept in one string slice per line without the termination line-break. + /// + /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding + /// doc-comment leader: `///` or `/**`. + #[cfg(feature = "metadata")] + #[inline] + pub fn update_fn_metadata_with_comments( + &mut self, + hash_fn: u64, + arg_names: &[impl AsRef], + comments: &[impl AsRef], + ) -> &mut Self { + self.update_fn_metadata(hash_fn, arg_names); + + if !comments.is_empty() { + let f = self.functions.get_mut(&hash_fn).expect("exists"); + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + } + + self + } + /// Update the namespace of a registered function. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. @@ -759,7 +797,7 @@ impl Module { #[cfg(feature = "metadata")] return_type_name, #[cfg(feature = "metadata")] - comments: crate::SmartString::new_const(), + comments: None, func: func.into(), } .into(), @@ -788,25 +826,32 @@ impl Module { /// /// The _last entry_ in the list should be the _return type_ of the function. /// In other words, the number of entries should be one larger than the number of parameters. + /// + /// ## Comments + /// + /// Block doc-comments should be kept in a single line. + /// + /// Line doc-comments should be kept in one string slice per line without the termination line-break. + /// + /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding + /// doc-comment leader: `///` or `/**`. #[cfg(feature = "metadata")] #[inline] - pub fn set_fn_with_comment( + pub fn set_fn_with_comments( &mut self, name: impl AsRef + Into, namespace: FnNamespace, access: FnAccess, arg_names: Option<&[&str]>, arg_types: &[TypeId], - comment: impl Into, + comments: &[impl AsRef], func: CallableFunction, ) -> u64 { let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); - let comment = comment.into(); - - if !comment.is_empty() { + if !comments.is_empty() { let f = self.functions.get_mut(&hash).expect("exists"); - f.comments = comment; + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } hash @@ -845,7 +890,7 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -953,7 +998,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -990,7 +1036,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1036,7 +1083,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1097,7 +1145,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1158,7 +1207,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index d214a701..f5c5a8dc 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -168,7 +168,9 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { } #[cfg(feature = "metadata")] { - info.comments.split("\n").collect() + info.comments + .as_ref() + .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } }, }