From 40b6a014ae1de0e11744a72d0fe8a30ca0f216fa Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 12 Dec 2020 11:47:18 +0800 Subject: [PATCH] Provide Position to debug. --- RELEASES.md | 2 ++ doc/src/language/print-debug.md | 12 ++++++++---- src/engine.rs | 19 ++++++++++++++----- src/engine_api.rs | 13 +++++++++---- src/fn_call.rs | 17 ++++++++++------- src/fn_native.rs | 9 ++++++++- tests/print.rs | 12 ++++++++---- 7 files changed, 59 insertions(+), 25 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 62fc4be6..7988a792 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -14,11 +14,13 @@ Breaking changes ---------------- * `Engine::on_progress` now takes `u64` instead of `&u64`. +* The closure for `Engine::on_debug` now takes an additional `Position` parameter. Enhancements ------------ * Capturing a constant variable in a closure is now supported, with no cloning. +* Provides position info for `debug` statements. Version 0.19.7 diff --git a/doc/src/language/print-debug.md b/doc/src/language/print-debug.md index 515b098f..606452d6 100644 --- a/doc/src/language/print-debug.md +++ b/doc/src/language/print-debug.md @@ -22,10 +22,12 @@ When embedding Rhai into an application, it is usually necessary to trap `print` (for logging into a tracking log, for example) with the `Engine::on_print` and `Engine::on_debug` methods: ```rust -// Any function or closure that takes an '&str' argument can be used to override -// 'print' and 'debug' +// Any function or closure that takes an '&str' argument can be used to override 'print'. engine.on_print(|x| println!("hello: {}", x)); -engine.on_debug(|x| println!("DEBUG: {}", x)); + +// Any function or closure that takes a '&str' and a 'Position' argument can be used to +// override 'debug'. +engine.on_debug(|x, pos| println!("DEBUG at {:?}: {}", pos, x)); // Example: quick-'n-dirty logging let logbook = Arc::new(RwLock::new(Vec::::new())); @@ -35,7 +37,9 @@ let log = logbook.clone(); engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s))); let log = logbook.clone(); -engine.on_debug(move |s| log.write().unwrap().push(format!("DEBUG: {}", s))); +engine.on_debug(move |s, pos| log.write().unwrap().push( + format!("DEBUG at {:?}: {}", pos, s) + )); // Evaluate script engine.eval::<()>(script)?; diff --git a/src/engine.rs b/src/engine.rs index 38a62918..958cf1d9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,7 +4,8 @@ use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{ - CallableFunction, IteratorFn, OnPrintCallback, OnProgressCallback, OnVarCallback, + CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback, + OnVarCallback, }; use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; @@ -626,7 +627,7 @@ pub struct Engine { /// Callback closure for implementing the `print` command. pub(crate) print: OnPrintCallback, /// Callback closure for implementing the `debug` command. - pub(crate) debug: OnPrintCallback, + pub(crate) debug: OnDebugCallback, /// Callback closure for progress reporting. pub(crate) progress: Option, @@ -677,7 +678,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool { fn_name.starts_with(FN_ANONYMOUS) } -/// Print/debug to stdout +/// Print to stdout #[inline(always)] fn default_print(_s: &str) { #[cfg(not(feature = "no_std"))] @@ -685,6 +686,14 @@ fn default_print(_s: &str) { println!("{}", _s); } +/// Debug to stdout +#[inline(always)] +fn default_debug(_s: &str, _pos: Position) { + #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + println!("{:?} | {}", _pos, _s); +} + /// Search for a module within an imports stack. /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub fn search_imports( @@ -741,7 +750,7 @@ impl Engine { // default print/debug implementations print: Box::new(default_print), - debug: Box::new(default_print), + debug: Box::new(default_debug), // progress callback progress: None, @@ -797,7 +806,7 @@ impl Engine { resolve_var: None, print: Box::new(|_| {}), - debug: Box::new(|_| {}), + debug: Box::new(|_, _| {}), progress: None, optimization_level: if cfg!(feature = "no_optimize") { diff --git a/src/engine_api.rs b/src/engine_api.rs index 5cdaf1e2..118c4dcc 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1808,16 +1808,21 @@ impl Engine { /// /// // Override action of 'print' function /// let logger = result.clone(); - /// engine.on_debug(move |s| logger.write().unwrap().push_str(s)); + /// engine.on_debug(move |s, pos| logger.write().unwrap().push_str( + /// &format!("{:?} > {}", pos, s) + /// )); /// - /// engine.consume(r#"debug("hello");"#)?; + /// engine.consume(r#"let x = "hello"; debug(x);"#)?; /// - /// assert_eq!(*result.read().unwrap(), r#""hello""#); + /// assert_eq!(*result.read().unwrap(), r#"1:18 > "hello""#); /// # Ok(()) /// # } /// ``` #[inline(always)] - pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self { + pub fn on_debug( + &mut self, + callback: impl Fn(&str, Position) + SendSync + 'static, + ) -> &mut Self { self.debug = Box::new(callback); self } diff --git a/src/fn_call.rs b/src/fn_call.rs index 9c498f29..85330a81 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -211,13 +211,16 @@ impl Engine { false, ), KEYWORD_DEBUG => ( - (self.debug)(result.as_str().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType( - self.map_type_name(type_name::()).into(), - typ.into(), - pos, - ) - })?) + (self.debug)( + result.as_str().map_err(|typ| { + EvalAltResult::ErrorMismatchOutputType( + self.map_type_name(type_name::()).into(), + typ.into(), + pos, + ) + })?, + pos, + ) .into(), false, ), diff --git a/src/fn_native.rs b/src/fn_native.rs index df74e163..c9c82300 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -356,7 +356,14 @@ pub type OnProgressCallback = Box Option + Send + Sync + pub type OnPrintCallback = Box; /// A standard callback function for printing. #[cfg(feature = "sync")] -pub type OnPrintCallback = Box; +pub type OnPrintCallback = Box; + +/// A standard callback function for debugging. +#[cfg(not(feature = "sync"))] +pub type OnDebugCallback = Box; +/// A standard callback function for debugging. +#[cfg(feature = "sync")] +pub type OnDebugCallback = Box; /// A standard callback function for variable access. #[cfg(not(feature = "sync"))] diff --git a/tests/print.rs b/tests/print.rs index 3d4c34a7..57471323 100644 --- a/tests/print.rs +++ b/tests/print.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, RegisterFn, INT}; use std::sync::{Arc, RwLock}; #[test] -fn test_print() -> Result<(), Box> { +fn test_print_debug() -> Result<(), Box> { let logbook = Arc::new(RwLock::new(Vec::::new())); // Redirect print/debug output to 'log' @@ -13,16 +13,20 @@ fn test_print() -> Result<(), Box> { engine .on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s))) - .on_debug(move |s| log2.write().unwrap().push(format!("DEBUG: {}", s))); + .on_debug(move |s, pos| { + log2.write() + .unwrap() + .push(format!("DEBUG at {:?}: {}", pos, s)) + }); // Evaluate script engine.eval::<()>("print(40 + 2)")?; - engine.eval::<()>(r#"debug("hello!")"#)?; + engine.eval::<()>(r#"let x = "hello!"; debug(x)"#)?; // 'logbook' captures all the 'print' and 'debug' output assert_eq!(logbook.read().unwrap().len(), 2); assert_eq!(logbook.read().unwrap()[0], "entry: 42"); - assert_eq!(logbook.read().unwrap()[1], r#"DEBUG: "hello!""#); + assert_eq!(logbook.read().unwrap()[1], r#"DEBUG at 1:19: "hello!""#); for entry in logbook.read().unwrap().iter() { println!("{}", entry);