diff --git a/minijinja/src/lib.rs b/minijinja/src/lib.rs index d05e29e6..11b43c9a 100644 --- a/minijinja/src/lib.rs +++ b/minijinja/src/lib.rs @@ -110,7 +110,8 @@ //! - `sync`: this feature makes MiniJinja's type `Send` and `Sync`. If this feature //! is disabled sending types across threads is often not possible. Thread bounds //! of things like callbacks however are not changing which means code that uses -//! MiniJinja still needs to be threadsafe. +//! MiniJinja still needs to be threadsafe. This also disables some features that +//! require synchronization such as the `loop.changed` feature. //! - `debug`: if this feature is removed some debug functionality of the engine is //! removed as well. This mainly affects the quality of error reporting. //! - `key_interning`: if this feature is removed the automatic string interning in diff --git a/minijinja/src/syntax.rs b/minijinja/src/syntax.rs index 06a01378..309c6b8c 100644 --- a/minijinja/src/syntax.rs +++ b/minijinja/src/syntax.rs @@ -195,9 +195,11 @@ //! - `loop.cycle`: A helper function to cycle between a list of sequences. See the explanation below. //! - `loop.depth`: Indicates how deep in a recursive loop the rendering currently is. Starts at level 1 //! - `loop.depth0`: Indicates how deep in a recursive loop the rendering currently is. Starts at level 0 +//! - `loop.changed(...args)`: Returns true if the passed values have changed since the last time it was called with the same arguments. +//! - `loop.cycle(...args)`: Returns a value from the passed sequence in a cycle. //! //! Within a for-loop, it’s possible to cycle among a list of strings/variables each time through -//! the loop by using the special loop.cycle helper: +//! the loop by using the special `loop.cycle` helper: //! //! ```jinja //! {% for row in rows %} @@ -205,6 +207,19 @@ //! {% endfor %} //! ``` //! +//! If the `sync` feature is not disabled, the `loop.changed` helper is also available +//! which can be used to detect when a value changes between the last iteration and the +//! current one. The method takes one or more arguments that are all compared. +//! +//! ```jinja +//! {% for entry in entries %} +//! {% if loop.changed(entry.category) %} +//!
{{ entry.message }}
+//! {% endfor %} +//! ``` +//! //! Unlike in Rust or Python, it’s not possible to break or continue in a loop. You can, //! however, filter the sequence during iteration, which allows you to skip items. The //! following example skips all the users which are hidden: diff --git a/minijinja/src/vm.rs b/minijinja/src/vm.rs index 1c4b2c09..410743d9 100644 --- a/minijinja/src/vm.rs +++ b/minijinja/src/vm.rs @@ -1,6 +1,8 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::{self, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "sync")] +use std::sync::Mutex; use crate::environment::Environment; use crate::error::{Error, ErrorKind}; @@ -16,6 +18,8 @@ pub struct LoopState { len: usize, idx: AtomicUsize, depth: usize, + #[cfg(feature = "sync")] + last_changed_value: Mutex