From 333392e034c718ff15690fd49c54ff342aae2f60 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 24 Sep 2018 13:43:19 -0700 Subject: [PATCH] Add immut! macros --- src/lib.rs | 4 +- src/utils/immutify.rs | 135 +++++++++++++++++++++++++++++++++++++++++ src/utils/mod.rs | 2 + src/views/edit_view.rs | 11 +--- 4 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 src/utils/immutify.rs diff --git a/src/lib.rs b/src/lib.rs index a5d0d8c..23a72d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,12 +103,14 @@ pub mod event; #[macro_use] pub mod view; +#[macro_use] +pub mod utils; + pub mod align; pub mod direction; pub mod menu; pub mod rect; pub mod theme; -pub mod utils; pub mod vec; pub mod views; diff --git a/src/utils/immutify.rs b/src/utils/immutify.rs new file mode 100644 index 0000000..72903ad --- /dev/null +++ b/src/utils/immutify.rs @@ -0,0 +1,135 @@ +/* + +// TODO: replace the 3 macros with 3 functions once they work correctly with +// reference arguments. +// (The returned closure must implement the for<'a> Fn(T, U<'a>)... + + +// TODO: replace the 3 macros/functions with a generic function when it can +// accept any number of arguments. + +/// Wraps a `FnMut` into a `Fn` +/// +/// This can be used to use a `FnMut` when a callack expects a `Fn`. +/// +/// # Note +/// +/// If the resulting `Fn` is called recursively, subsequent calls will be +/// no-ops. +pub fn immutify( + f: F, +) -> impl for<'s> Fn(&'s mut Cursive) { + let callback = RefCell::new(f); + move |s| { + // Here's the weird trick: if we're already borrowed, + // just ignored the callback. + if let Ok(mut f) = callback.try_borrow_mut() { + // Beeeaaah that's ugly. + // Why do we need to manually dereference here? + (&mut *f)(s); + } + } +} + +*/ + +/// Macro to wrap a `FnMut` with 1 argument into a `Fn`. +/// +/// This can wrap any `FnMut` with a single arguments (for example `&mut Cursive`). +/// +/// See [`immut2!`] and [`immut3!`] to support a different number of arguments. +/// +/// # Note +/// +/// If this function tries to call itself recursively (for example by +/// triggering an event in `Cursive`), the second call will be a no-op. +/// Enabling recursive calls would break the `FnMut` contract. +/// +/// In addition, due to weird interaction between Higher-rank trait bounds and +/// closures, you should use the result from the macro directly, and not +/// assign it to a variable. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate cursive; +/// # use cursive::Cursive; +/// # fn main() { +/// # let mut siv = Cursive::dummy(); +/// let mut i = 0; +/// // `Cursive::add_global_callback` takes a `Fn(&mut Cursive)` +/// siv.add_global_callback('q', immut1!(move |s: &mut Cursive| { +/// // But here we mutate the environment! Crazy! +/// i += 1; +/// if i == 5 { +/// s.quit(); +/// } +/// })); +/// # } +/// ``` +#[macro_export] +macro_rules! immut1 { + ($f:expr) => {{ + let callback = ::std::cell::RefCell::new($f); + move |s| { + if let Ok(mut f) = callback.try_borrow_mut() { + (&mut *f)(s) + } + } + }}; +} + +/// Macro to wrap a `FnMut` with 2 arguments into a `Fn`. +/// +/// This can wrap any `FnMut` with two arguments. +/// +/// See [`immut1!`] and [`immut3!`] to support a different number of arguments. +/// +/// # Note +/// +/// If this function tries to call itself recursively (for example by +/// triggering an event in `Cursive`), the second call will be a no-op. +/// Enabling recursive calls would break the `FnMut` contract. +/// +/// In addition, due to weird interaction between Higher-rank trait bounds and +/// closures, you should use the result from the macro directly, and not +/// assign it to a variable. +#[macro_export] +macro_rules! immut2 { + ($f:expr) => {{ + let callback = ::std::cell::RefCell::new($f); + move |s, t| { + if let Ok(mut f) = callback.try_borrow_mut() { + (&mut *f)(s, t) + } + } + }}; +} + +/// Macro to wrap a `FnMut` with 3 arguments into a `Fn`. +/// +/// This can wrap any `FnMut` with three arguments. +/// +/// See [`immut1!`] and [`immut2!`] to support a different number of arguments. +/// +/// # Note +/// +/// If this function tries to call itself recursively (for example by +/// triggering an event in `Cursive`), the second call will be a no-op. +/// Enabling recursive calls would break the `FnMut` contract. +/// +/// In addition, due to weird interaction between Higher-rank trait bounds and +/// closures, you should use the result from the macro directly, and not +/// assign it to a variable. +#[macro_export] +macro_rules! immut3 { + ($f:expr) => {{ + let callback = ::std::cell::RefCell::new($f); + move |s, t, u| { + if let Ok(mut f) = callback.try_borrow_mut() { + (&mut *f)(s, t, u) + } + } + }}; +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 88ee597..b5d30e2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,8 @@ //! Toolbox to make text layout easier. mod counter; +#[macro_use] +mod immutify; pub mod lines; pub mod markup; mod reader; diff --git a/src/views/edit_view.rs b/src/views/edit_view.rs index 0df46db..073aaf9 100644 --- a/src/views/edit_view.rs +++ b/src/views/edit_view.rs @@ -236,16 +236,7 @@ impl EditView { where F: FnMut(&mut Cursive, &str, usize) + 'static, { - let callback = RefCell::new(callback); - // Here's the weird trick: if we're already borrowed, - // just ignored the callback. - self.set_on_edit(move |s, text, cursor| { - if let Ok(mut f) = callback.try_borrow_mut() { - // Beeeaaah that's ugly. - // Why do we need to manually dereference here? - (&mut *f)(s, text, cursor); - } - }); + self.set_on_edit(immut3!(callback)); } /// Sets a callback to be called whenever the content is modified.