diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md index 7eda240e..18ffef7d 100644 --- a/docs/content/docs/_index.md +++ b/docs/content/docs/_index.md @@ -112,7 +112,7 @@ tera.autoescape_on(vec![".php.html"]); tera.autoescape_on(vec![]); ``` -Tera does not perform contextual auto-escaping, e.g. by parsing the template to know whether to escape JS, CSS or HTML (see +Tera does not perform contextual auto-escaping, e.g. by parsing the template to know whether to escape JS, CSS or HTML (see for more details on that). ## Advanced usage @@ -200,7 +200,7 @@ would be rendered as `Hello {{ name }}`. ### Whitespace control Tera comes with easy to use whitespace control: use `{%-` if you want to remove all whitespace -before a statement and `-%}` if you want to remove all whitespace after. This behavior also +before a statement and `-%}` if you want to remove all whitespace after. This behavior also works with expressions, using `{{-` and `-}}`, and with comments, using `{#-` and `-#}`. For example, let's look at the following template: @@ -854,6 +854,9 @@ By default, the filter will add an ellipsis at the end if the text was truncated change the string appended by setting the `end` argument. For example, `{{ value | truncate(length=10, end="") }}` will not append anything. +In addition, to truncate but not cut off mid-word, you can set `killwords` argument to `true`. +For example, `{{ value | truncate(length=10, killwords=true) }}` will truncate the string and discard the last word. + #### linebreaksbr Replaces line breaks (`\n` or `\r\n`) with HTML line breaks (`
`). diff --git a/src/builtins/filters/string.rs b/src/builtins/filters/string.rs index d874bd6d..fa99c479 100644 --- a/src/builtins/filters/string.rs +++ b/src/builtins/filters/string.rs @@ -148,6 +148,8 @@ pub fn trim_end_matches(value: &Value, args: &HashMap) -> Result< /// returned untouched. The default value is 255. /// * `end` - The ellipsis string to be used if the given string is /// truncated. The default value is "…". +/// * `killwords` - If true, the string will be truncated by discarding the +/// last word. The default value is false. /// /// # Remarks /// @@ -164,6 +166,10 @@ pub fn truncate(value: &Value, args: &HashMap) -> Result { Some(l) => try_get_value!("truncate", "end", String, l), None => "…".to_string(), }; + let killwords = match args.get("killwords") { + Some(l) => try_get_value!("truncate", "killwords", bool, l), + None => false, + }; let graphemes = GraphemeIndices::new(&s).collect::>(); @@ -172,7 +178,13 @@ pub fn truncate(value: &Value, args: &HashMap) -> Result { return Ok(to_value(&s).unwrap()); } - let result = s[..graphemes[length].0].to_string() + &end; + let mut result = s[..graphemes[length].0].to_string(); + if killwords { + let last_word = result.rfind(' ').unwrap_or(result.len()); + result.truncate(last_word); + } + result = result + &end; + Ok(to_value(result).unwrap()) } @@ -557,6 +569,29 @@ mod tests { assert_eq!(result.unwrap(), to_value("πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ fam…").unwrap()); } + #[test] + fn test_truncate_killwords() { + let mut args = HashMap::new(); + args.insert("length".to_string(), to_value(24).unwrap()); + args.insert("killwords".to_string(), to_value(true).unwrap()); + let result = truncate( + &to_value("Lorem ipsum dolor sit amet, consectetur adipiscing elit.").unwrap(), + &args, + ); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), to_value("Lorem ipsum dolor sit…").unwrap()); + } + + #[test] + fn test_truncate_killwords_but_shorter_than_length() { + let mut args = HashMap::new(); + args.insert("length".to_string(), to_value(12).unwrap()); + args.insert("killwords".to_string(), to_value(true).unwrap()); + let result = truncate(&to_value("Lorem ipsum").unwrap(), &args); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), to_value("Lorem ipsum").unwrap()); + } + #[test] fn test_lower() { let result = lower(&to_value("HELLO").unwrap(), &HashMap::new());