diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a6e926..1ebb3d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ New features * A new feature, `no_custom_syntax`, is added to remove custom syntax support from Rhai for applications that do not require it (which should be most). * Comment lines beginning with `//!` (requires the `metadata` feature) are now collected as the script file's _module documentation_. +* `AST` and `Module` have methods to access and manipulate documentation. Enhancements ------------ @@ -34,6 +35,7 @@ Enhancements * A new `range` function variant that takes an exclusive range with a step. * `as_string` is added to BLOB's to convert it into a string by interpreting it as a UTF-8 byte stream. * `FnAccess::is_private`, `FnAccess::is_public`, `FnNamespace::is_module_namespace` and `FnNameSpace::is_global_namespace` are added for convenience. +* `Iterator` type for functions metadata is simplified to `Iterator`. Version 1.8.0 diff --git a/Cargo.toml b/Cargo.toml index 7678fb27..9d421c64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,10 @@ name = "rhai-run" name = "rhai-dbg" required-features = ["debugging"] +[[example]] +name = "serde" +required-features = ["serde"] + [profile.release] lto = "fat" codegen-units = 1 diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index 1cfeac47..fc937bcd 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -1,5 +1,10 @@ //! An example showing how to register a Rust type and use it with arrays. +#[cfg(any(feature = "no_index", feature = "no_object"))] +fn main() { + panic!("This example does not run under 'no_index' or 'no_object'.") +} + use rhai::{Engine, EvalAltResult}; #[cfg(not(feature = "no_index"))] @@ -60,8 +65,3 @@ fn main() -> Result<(), Box> { Ok(()) } - -#[cfg(any(feature = "no_index", feature = "no_object"))] -fn main() { - panic!("This example does not run under 'no_index' or 'no_object'.") -} diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index e8261a73..a70265ff 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -1,5 +1,10 @@ //! An example showing how to register a Rust type and methods/getters/setters for it. +#[cfg(feature = "no_object")] +fn main() { + panic!("This example does not run under 'no_object'."); +} + use rhai::{Engine, EvalAltResult}; #[cfg(not(feature = "no_object"))] @@ -61,8 +66,3 @@ fn main() -> Result<(), Box> { Ok(()) } - -#[cfg(feature = "no_object")] -fn main() { - panic!("This example does not run under 'no_object'."); -} diff --git a/examples/event_handler_js/main.rs b/examples/event_handler_js/main.rs index 99118feb..db5740f3 100644 --- a/examples/event_handler_js/main.rs +++ b/examples/event_handler_js/main.rs @@ -1,43 +1,45 @@ //! Implementation of the Event Handler With State Pattern - JS Style -use rhai::{Dynamic, Engine, Scope, AST}; -#[cfg(not(feature = "no_object"))] -use rhai::Map; - -use std::io::{stdin, stdout, Write}; - -const SCRIPT_FILE: &str = "event_handler_js/script.rhai"; - -#[derive(Debug)] -struct Handler { - pub engine: Engine, - pub scope: Scope<'static>, - pub states: Dynamic, - pub ast: AST, -} - -fn print_scope(scope: &Scope) { - for (i, (name, constant, value)) in scope.iter_raw().enumerate() { - #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; - #[cfg(feature = "no_closure")] - let value_is_shared = ""; - - println!( - "[{}] {}{}{} = {:?}", - i + 1, - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ) - } - println!(); +#[cfg(any(feature = "no_function", feature = "no_object"))] +pub fn main() { + panic!("This example does not run under 'no_function' or 'no_object'.") } #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_object"))] pub fn main() { + use rhai::{Dynamic, Engine, Map, Scope, AST}; + use std::io::{stdin, stdout, Write}; + + const SCRIPT_FILE: &str = "event_handler_js/script.rhai"; + + #[derive(Debug)] + struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub states: Dynamic, + pub ast: AST, + } + + fn print_scope(scope: &Scope) { + for (i, (name, constant, value)) in scope.iter_raw().enumerate() { + #[cfg(not(feature = "no_closure"))] + let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; + #[cfg(feature = "no_closure")] + let value_is_shared = ""; + + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + value_is_shared, + *value.read_lock::().unwrap(), + ) + } + println!(); + } + println!("Events Handler Example - JS Style"); println!("=================================="); @@ -158,8 +160,3 @@ pub fn main() { println!("Bye!"); } - -#[cfg(any(feature = "no_function", feature = "no_object"))] -pub fn main() { - panic!("This example does not run under 'no_function' or 'no_object'.") -} diff --git a/examples/event_handler_main/main.rs b/examples/event_handler_main/main.rs index 76348974..bc64a8b6 100644 --- a/examples/event_handler_main/main.rs +++ b/examples/event_handler_main/main.rs @@ -1,38 +1,43 @@ //! Implementation of the Event Handler With State Pattern - Main Style -use rhai::{Dynamic, Engine, Scope, AST}; -use std::io::{stdin, stdout, Write}; - -const SCRIPT_FILE: &str = "event_handler_main/script.rhai"; - -#[derive(Debug)] -struct Handler { - pub engine: Engine, - pub scope: Scope<'static>, - pub ast: AST, -} - -fn print_scope(scope: &Scope) { - for (i, (name, constant, value)) in scope.iter_raw().enumerate() { - #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; - #[cfg(feature = "no_closure")] - let value_is_shared = ""; - - println!( - "[{}] {}{}{} = {:?}", - i + 1, - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ) - } - println!(); +#[cfg(feature = "no_function")] +pub fn main() { + panic!("This example does not run under 'no_function'.") } #[cfg(not(feature = "no_function"))] pub fn main() { + use rhai::{Dynamic, Engine, Scope, AST}; + use std::io::{stdin, stdout, Write}; + + const SCRIPT_FILE: &str = "event_handler_main/script.rhai"; + + #[derive(Debug)] + struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub ast: AST, + } + + fn print_scope(scope: &Scope) { + for (i, (name, constant, value)) in scope.iter_raw().enumerate() { + #[cfg(not(feature = "no_closure"))] + let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; + #[cfg(feature = "no_closure")] + let value_is_shared = ""; + + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + value_is_shared, + *value.read_lock::().unwrap(), + ) + } + println!(); + } + println!("Events Handler Example - Main Style"); println!("==================================="); @@ -130,8 +135,3 @@ pub fn main() { println!("Bye!"); } - -#[cfg(feature = "no_function")] -pub fn main() { - panic!("This example does not run under 'no_function'.") -} diff --git a/examples/event_handler_map/main.rs b/examples/event_handler_map/main.rs index 1e7d1102..ad27e8ea 100644 --- a/examples/event_handler_map/main.rs +++ b/examples/event_handler_map/main.rs @@ -1,42 +1,44 @@ //! Implementation of the Event Handler With State Pattern - Map Style -use rhai::{Dynamic, Engine, Scope, AST}; -#[cfg(not(feature = "no_object"))] -use rhai::Map; - -use std::io::{stdin, stdout, Write}; - -const SCRIPT_FILE: &str = "event_handler_map/script.rhai"; - -#[derive(Debug)] -struct Handler { - pub engine: Engine, - pub scope: Scope<'static>, - pub ast: AST, -} - -fn print_scope(scope: &Scope) { - for (i, (name, constant, value)) in scope.iter_raw().enumerate() { - #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; - #[cfg(feature = "no_closure")] - let value_is_shared = ""; - - println!( - "[{}] {}{}{} = {:?}", - i + 1, - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ) - } - println!(); +#[cfg(any(feature = "no_function", feature = "no_object"))] +pub fn main() { + panic!("This example does not run under 'no_function' or 'no_object'.") } #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_object"))] pub fn main() { + use rhai::{Dynamic, Engine, Map, Scope, AST}; + use std::io::{stdin, stdout, Write}; + + const SCRIPT_FILE: &str = "event_handler_map/script.rhai"; + + #[derive(Debug)] + struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub ast: AST, + } + + fn print_scope(scope: &Scope) { + for (i, (name, constant, value)) in scope.iter_raw().enumerate() { + #[cfg(not(feature = "no_closure"))] + let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; + #[cfg(feature = "no_closure")] + let value_is_shared = ""; + + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + value_is_shared, + *value.read_lock::().unwrap(), + ) + } + println!(); + } + println!("Events Handler Example - Map Style"); println!("=================================="); @@ -147,8 +149,3 @@ pub fn main() { println!("Bye!"); } - -#[cfg(any(feature = "no_function", feature = "no_object"))] -pub fn main() { - panic!("This example does not run under 'no_function' or 'no_object'.") -} diff --git a/examples/serde.rs b/examples/serde.rs index 625bb5f6..b145e247 100644 --- a/examples/serde.rs +++ b/examples/serde.rs @@ -1,22 +1,12 @@ //! An example to serialize and deserialize Rust types. -#[cfg(any(not(feature = "serde"), feature = "no_object"))] +#[cfg(feature = "no_object")] fn main() { - println!("This example requires the 'serde' feature to run."); - println!("Try: cargo run --features serde --example serde"); + panic!("This example does not run under 'no_object'.") } -#[cfg(feature = "serde")] #[cfg(not(feature = "no_object"))] fn main() { - example::ser(); - println!(); - example::de(); -} - -#[cfg(feature = "serde")] -#[cfg(not(feature = "no_object"))] -mod example { use rhai::serde::{from_dynamic, to_dynamic}; use rhai::{Dynamic, Engine, Map}; use serde::{Deserialize, Serialize}; @@ -60,13 +50,13 @@ mod example { let result: Dynamic = engine .eval( r#" - #{ - a: 42, - b: [ "hello", "world" ], - c: true, - d: #{ x: 123.456, y: 999.0 } - } - "#, + #{ + a: 42, + b: [ "hello", "world" ], + c: true, + d: #{ x: 123.456, y: 999.0 } + } + "#, ) .unwrap(); @@ -89,4 +79,8 @@ mod example { ); println!("Deserialized to struct: {:#?}", x); } + + ser(); + println!(); + de(); } diff --git a/src/api/compile.rs b/src/api/compile.rs index b9e95e50..78424402 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -222,10 +222,10 @@ impl Engine { self.token_mapper.as_ref().map(<_>::as_ref), ); let mut state = ParseState::new(self, scope, tokenizer_control); - let mut ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?; + let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?; #[cfg(feature = "metadata")] - ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n")); - Ok(ast) + _ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n")); + Ok(_ast) } /// Compile a string containing an expression into an [`AST`], /// which can be used later for evaluation. diff --git a/src/api/optimize.rs b/src/api/optimize.rs index 726b12f9..f790a803 100644 --- a/src/api/optimize.rs +++ b/src/api/optimize.rs @@ -2,7 +2,6 @@ #![cfg(not(feature = "no_optimize"))] use crate::{Engine, OptimizationLevel, Scope, AST}; -use std::mem; impl Engine { /// Control whether and how the [`Engine`] will optimize an [`AST`] after compilation. @@ -60,7 +59,7 @@ impl Engine { .map(|f| f.func.get_script_fn_def().unwrap().clone()) .collect(); - let mut new_ast = crate::optimizer::optimize_into_ast( + let mut _new_ast = crate::optimizer::optimize_into_ast( self, scope, ast.take_statements(), @@ -70,8 +69,8 @@ impl Engine { ); #[cfg(feature = "metadata")] - new_ast.set_doc(mem::take(ast.doc_mut())); + _new_ast.set_doc(std::mem::take(ast.doc_mut())); - new_ast + _new_ast } } diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 0c201593..854ce431 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -1,7 +1,7 @@ //! Module defining the AST (abstract syntax tree). use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer}; -use crate::{Dynamic, FnNamespace, Identifier, Position, SmartString}; +use crate::{Dynamic, FnNamespace, Identifier, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -23,7 +23,7 @@ pub struct AST { source: Identifier, /// [`AST`] documentation. #[cfg(feature = "metadata")] - doc: SmartString, + doc: crate::SmartString, /// Global statements. body: StmtBlock, /// Script-defined functions. @@ -76,7 +76,7 @@ impl AST { Self { source: Identifier::new_const(), #[cfg(feature = "metadata")] - doc: SmartString::new_const(), + doc: crate::SmartString::new_const(), body: StmtBlock::new(statements, Position::NONE, Position::NONE), #[cfg(not(feature = "no_function"))] lib: functions.into(), @@ -96,7 +96,7 @@ impl AST { Self { source: Identifier::new_const(), #[cfg(feature = "metadata")] - doc: SmartString::new_const(), + doc: crate::SmartString::new_const(), body: StmtBlock::new(statements, Position::NONE, Position::NONE), #[cfg(not(feature = "no_function"))] lib: functions.into(), @@ -146,7 +146,7 @@ impl AST { Self { source: Identifier::new_const(), #[cfg(feature = "metadata")] - doc: SmartString::new_const(), + doc: crate::SmartString::new_const(), body: StmtBlock::NONE, #[cfg(not(feature = "no_function"))] lib: crate::Module::new().into(), @@ -195,6 +195,7 @@ impl AST { /// Leading white-spaces are stripped, and each line always starts with `//!`. #[cfg(feature = "metadata")] #[inline(always)] + #[must_use] pub fn doc(&self) -> &str { &self.doc } @@ -211,7 +212,8 @@ impl AST { /// Only available under `metadata`. #[cfg(feature = "metadata")] #[inline(always)] - pub(crate) fn doc_mut(&mut self) -> &mut SmartString { + #[must_use] + pub(crate) fn doc_mut(&mut self) -> &mut crate::SmartString { &mut self.doc } /// Set the documentation. @@ -219,7 +221,7 @@ impl AST { /// Only available under `metadata`. #[cfg(feature = "metadata")] #[inline(always)] - pub(crate) fn set_doc(&mut self, doc: impl Into) { + pub(crate) fn set_doc(&mut self, doc: impl Into) { self.doc = doc.into(); } /// Get the statements. diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index c2cad9da..ae9b064a 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -260,7 +260,7 @@ impl RangeCase { Self::InclusiveInt(..) => true, } } - /// Get the index to the [`ConditionalStmtBlock`]. + /// Get the index to the [`ConditionalExpr`]. #[inline(always)] #[must_use] pub fn index(&self) -> usize { @@ -268,7 +268,7 @@ impl RangeCase { Self::ExclusiveInt(.., n) | Self::InclusiveInt(.., n) => *n, } } - /// Set the index to the [`ConditionalStmtBlock`]. + /// Set the index to the [`ConditionalExpr`]. #[inline(always)] pub fn set_index(&mut self, index: usize) { match self { diff --git a/src/lib.rs b/src/lib.rs index e476fb15..7b8c9cff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -272,8 +272,8 @@ pub use tokenizer::{get_next_token, parse_string_literal}; #[cfg(feature = "internals")] pub use tokenizer::{ - InputStream, MultiInputsStream, Span, Token, TokenIterator, TokenizeState, TokenizerControl, - TokenizerControlBlock, + is_valid_function_name, is_valid_identifier, InputStream, MultiInputsStream, Span, Token, + TokenIterator, TokenizeState, TokenizerControl, TokenizerControlBlock, }; #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index 38bb887a..01ac556d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -250,6 +250,9 @@ pub struct Module { /// ID identifying the module. /// No ID if string is empty. id: Identifier, + /// Module documentation. + #[cfg(feature = "metadata")] + doc: crate::SmartString, /// Is this module internal? pub(crate) internal: bool, /// Is this module part of a standard library? @@ -290,31 +293,27 @@ impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Module"); - if !self.id.is_empty() { - d.field("id", &self.id); - } - if !self.modules.is_empty() { - d.field( + d.field("id", &self.id) + .field( "modules", &self .modules .keys() .map(|m| m.as_str()) .collect::>(), - ); - } - if !self.variables.is_empty() { - d.field("vars", &self.variables); - } - if !self.functions.is_empty() { - d.field( + ) + .field("vars", &self.variables) + .field( "functions", &self .iter_fn() .map(|f| f.func.to_string()) .collect::>(), ); - } + + #[cfg(feature = "metadata")] + d.field("doc", &self.doc); + d.finish() } } @@ -363,6 +362,8 @@ impl Module { pub fn new() -> Self { Self { id: Identifier::new_const(), + #[cfg(feature = "metadata")] + doc: crate::SmartString::new_const(), internal: false, standard: false, custom_types: CustomTypesCollection::new(), @@ -423,6 +424,7 @@ impl Module { self.id = id.into(); self } + /// Clear the ID of the [`Module`]. /// /// # Example @@ -441,10 +443,69 @@ impl Module { self } + /// Get the documentation of the [`Module`], if any. + /// Exported under the `metadata` feature only. + /// + /// # Example + /// + /// ``` + /// # use rhai::Module; + /// let mut module = Module::new(); + /// module.set_doc("//! This is my special module."); + /// assert_eq!(module.doc(), "//! This is my special module."); + /// ``` + #[cfg(feature = "metadata")] + #[inline] + #[must_use] + pub fn doc(&self) -> &str { + &self.doc + } + + /// Set the documentation of the [`Module`]. + /// Exported under the `metadata` feature only. + /// + /// If the string is empty, it is equivalent to clearing the documentation. + /// + /// # Example + /// + /// ``` + /// # use rhai::Module; + /// let mut module = Module::new(); + /// module.set_doc("//! This is my special module."); + /// assert_eq!(module.doc(), "//! This is my special module."); + /// ``` + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn set_doc(&mut self, doc: impl Into) -> &mut Self { + self.doc = doc.into(); + self + } + + /// Clear the documentation of the [`Module`]. + /// + /// # Example + /// + /// ``` + /// # use rhai::Module; + /// let mut module = Module::new(); + /// module.set_doc("//! This is my special module."); + /// assert_eq!(module.doc(), "//! This is my special module."); + /// module.clear_doc(); + /// assert_eq!(module.doc(), ""); + /// ``` + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn clear_doc(&mut self) -> &mut Self { + self.doc.clear(); + self + } + /// Clear the [`Module`]. #[inline(always)] pub fn clear(&mut self) { self.id.clear(); + #[cfg(feature = "metadata")] + self.doc.clear(); self.internal = false; self.standard = false; self.custom_types.clear(); @@ -1563,6 +1624,15 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; + + #[cfg(feature = "metadata")] + if !other.doc.is_empty() { + if !self.doc.is_empty() { + self.doc.push('\n'); + } + self.doc.push_str(&other.doc); + } + self } @@ -1584,6 +1654,15 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; + + #[cfg(feature = "metadata")] + if !other.doc.is_empty() { + if !self.doc.is_empty() { + self.doc.push('\n'); + } + self.doc.push_str(&other.doc); + } + self } @@ -1614,6 +1693,15 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; + + #[cfg(feature = "metadata")] + if !other.doc.is_empty() { + if !self.doc.is_empty() { + self.doc.push('\n'); + } + self.doc.push_str(&other.doc); + } + self } @@ -1667,6 +1755,15 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; + + #[cfg(feature = "metadata")] + if !other.doc.is_empty() { + if !self.doc.is_empty() { + self.doc.push('\n'); + } + self.doc.push_str(&other.doc); + } + self } @@ -1978,6 +2075,9 @@ impl Module { module.set_id(ast.source_raw().clone()); + #[cfg(feature = "metadata")] + module.set_doc(ast.doc()); + module.build_index(); Ok(module) diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 4f6d4317..28304579 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -248,7 +248,7 @@ macro_rules! reg_range { $lib.update_fn_metadata_with_comments(_hash, [ concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), - concat!("Iterator"), + concat!("Iterator<", stringify!($y), ">"), ], [ "/// Return an iterator over the exclusive range of `from..to`.", "/// The value `to` is never included.", @@ -282,7 +282,7 @@ macro_rules! reg_range { concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), concat!("step: ", stringify!($y)), - concat!("Iterator") + concat!("Iterator<", stringify!($y), ">") ], [ "/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.", "/// The value `to` is never included.", @@ -312,7 +312,7 @@ macro_rules! reg_range { $lib.update_fn_metadata_with_comments(_hash, [ concat!("range: Range<", stringify!($y), ">"), concat!("step: ", stringify!($y)), - concat!("Iterator") + concat!("Iterator<", stringify!($y), ">") ], [ "/// Return an iterator over an exclusive range, each iteration increasing by `step`.", "///", @@ -388,7 +388,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &str", &range_type, "Iterator"], + ["string: &str", &range_type, "Iterator"], [ "/// Return an iterator over an exclusive range of characters in the string.", "///", @@ -410,7 +410,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &str", &range_inclusive_type, "Iterator"], + ["string: &str", &range_inclusive_type, "Iterator"], [ "/// Return an iterator over an inclusive range of characters in the string.", "///", @@ -428,7 +428,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &str", "start: INT", "len: INT", "Iterator"], + ["string: &str", "start: INT", "len: INT", "Iterator"], [ "/// Return an iterator over a portion of characters in the string.", "///", @@ -452,7 +452,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &str", "from: INT", "Iterator"], + ["string: &str", "from: INT", "Iterator"], [ "/// Return an iterator over the characters in the string starting from the `start` position.", "///", @@ -474,7 +474,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &str", "Iterator"], + ["string: &str", "Iterator"], [ "/// Return an iterator over the characters in the string.", "///", @@ -494,7 +494,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["string: &mut ImmutableString", "Iterator"], + ["string: &mut ImmutableString", "Iterator"], [ "/// Return an iterator over all the characters in the string.", "///", @@ -520,7 +520,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: INT", &range_type, "Iterator"], + ["value: INT", &range_type, "Iterator"], [ "/// Return an iterator over an exclusive range of bits in the number.", "///", @@ -544,7 +544,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: INT", &range_inclusive_type, "Iterator"], + ["value: INT", &range_inclusive_type, "Iterator"], [ "/// Return an iterator over an inclusive range of bits in the number.", "///", @@ -564,7 +564,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: INT", "from: INT", "len: INT", "Iterator"], + ["value: INT", "from: INT", "len: INT", "Iterator"], [ "/// Return an iterator over a portion of bits in the number.", "///", @@ -588,7 +588,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: INT", "from: INT", "Iterator"], + ["value: INT", "from: INT", "Iterator"], [ "/// Return an iterator over the bits in the number starting from the specified `start` position.", "///", @@ -610,7 +610,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: INT", "Iterator"], + ["value: INT", "Iterator"], [ "/// Return an iterator over all the bits in the number.", "///", @@ -632,7 +632,7 @@ def_package! { #[cfg(feature = "metadata")] lib.update_fn_metadata_with_comments( _hash, - ["value: &mut INT", "Iterator"], + ["value: &mut INT", "Iterator"], [ "/// Return an iterator over all the bits in the number.", "///", diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index a82a5fa4..2390487d 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -174,6 +174,17 @@ fn collect_fn_metadata( .collect::() .into(), ); + #[cfg(feature = "metadata")] + if !func.comments.is_empty() { + map.insert( + dict.get("comments").expect(DICT).clone(), + func.comments + .iter() + .map(|s| Into::into(&**s)) + .collect::() + .into(), + ); + } map } @@ -188,6 +199,8 @@ fn collect_fn_metadata( "private", "is_anonymous", "params", + #[cfg(feature = "metadata")] + "comments", ] .iter() .map(|&s| s.into()) diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index f430e484..4c8cc501 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -3,7 +3,7 @@ #![cfg(feature = "metadata")] use crate::module::{calc_native_fn_hash, FuncInfo}; -use crate::{calc_fn_hash, Engine, AST}; +use crate::{calc_fn_hash, Engine, SmartString, AST}; use serde::{Deserialize, Serialize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -158,6 +158,9 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] struct ModuleMetadata<'a> { + #[cfg(feature = "metadata")] + #[serde(skip_serializing_if = "SmartString::is_empty")] + pub doc: SmartString, #[serde(skip_serializing_if = "BTreeMap::is_empty")] pub modules: BTreeMap<&'a str, Self>, #[serde(skip_serializing_if = "Vec::is_empty")] @@ -168,6 +171,8 @@ impl ModuleMetadata<'_> { #[inline(always)] pub fn new() -> Self { Self { + #[cfg(feature = "metadata")] + doc: SmartString::new_const(), modules: BTreeMap::new(), functions: Vec::new(), } @@ -180,6 +185,7 @@ impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> { functions.sort(); Self { + doc: module.doc().into(), modules: module .iter_sub_modules() .map(|(name, m)| (name, m.as_ref().into())) @@ -240,6 +246,11 @@ impl Engine { global.functions.sort(); + #[cfg(feature = "metadata")] + { + global.doc = ast.doc().into(); + } + serde_json::to_string_pretty(&global) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5b827c38..5cccd741 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2179,7 +2179,7 @@ fn get_identifier( Some((Token::Identifier(identifier.into()), start_pos)) } -/// Is this keyword allowed as a function? +/// Is a keyword allowed as a function? #[inline] #[must_use] pub fn is_keyword_function(name: &str) -> bool { @@ -2194,7 +2194,8 @@ pub fn is_keyword_function(name: &str) -> bool { } } -/// Is a text string a valid identifier? +/// _(internals)_ Is a text string a valid identifier? +/// Exported under the `internals` feature only. #[must_use] pub fn is_valid_identifier(name: impl Iterator) -> bool { let mut first_alphabetic = false; @@ -2212,7 +2213,8 @@ pub fn is_valid_identifier(name: impl Iterator) -> bool { first_alphabetic } -/// Is a text string a valid script-defined function name? +/// _(internals)_ Is a text string a valid script-defined function name? +/// Exported under the `internals` feature only. #[inline(always)] #[must_use] pub fn is_valid_function_name(name: &str) -> bool {