diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-24 19:56:01 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-24 19:56:01 +0100 |
| commit | efde5cac88078f10485f715be66a27efba2f23d8 (patch) | |
| tree | 6d8d8d778e88b317f0e85a8815f887757a445e36 /src/library | |
| parent | ecd2bca606c0533ec6426b03fc216df256d43c3f (diff) | |
Lower and upper on templates
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/text.rs | 33 | ||||
| -rw-r--r-- | src/library/utility.rs | 15 |
2 files changed, 43 insertions, 5 deletions
diff --git a/src/library/text.rs b/src/library/text.rs index ba7f4c20..448ba9af 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -95,6 +95,9 @@ impl TextNode { /// Whether a monospace font should be preferred. #[skip] pub const MONOSPACED: bool = false; + /// The case transformation that should be applied to the next. + #[skip] + pub const CASE: Option<Case> = None; /// Decorative lines. #[skip] #[fold(|a, b| a.into_iter().chain(b).collect())] @@ -384,6 +387,25 @@ castable! { .collect(), } +/// A case transformation on text. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum Case { + /// Everything is uppercased. + Upper, + /// Everything is lowercased. + Lower, +} + +impl Case { + /// Apply the case to a string of text. + pub fn apply(self, text: &str) -> String { + match self { + Self::Upper => text.to_uppercase(), + Self::Lower => text.to_lowercase(), + } + } +} + /// Shape text into [`ShapedText`]. pub fn shape<'a>( fonts: &mut FontStore, @@ -391,13 +413,18 @@ pub fn shape<'a>( styles: StyleChain<'a>, dir: Dir, ) -> ShapedText<'a> { + let text = match styles.get(TextNode::CASE) { + Some(case) => Cow::Owned(case.apply(text)), + None => Cow::Borrowed(text), + }; + let mut glyphs = vec![]; if !text.is_empty() { shape_segment( fonts, &mut glyphs, 0, - text, + &text, variant(styles), families(styles), None, @@ -743,7 +770,7 @@ fn tags(styles: StyleChain) -> Vec<Feature> { #[derive(Debug, Clone)] pub struct ShapedText<'a> { /// The text that was shaped. - pub text: &'a str, + pub text: Cow<'a, str>, /// The text direction. pub dir: Dir, /// The text's style properties. @@ -941,7 +968,7 @@ impl<'a> ShapedText<'a> { if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) { let (size, baseline) = measure(fonts, glyphs, self.styles); Self { - text: &self.text[text_range], + text: Cow::Borrowed(&self.text[text_range]), dir: self.dir, styles: self.styles.clone(), size, diff --git a/src/library/utility.rs b/src/library/utility.rs index 051dd885..ceae20bf 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -4,6 +4,7 @@ use std::cmp::Ordering; use std::str::FromStr; use super::prelude::*; +use super::{Case, TextNode}; use crate::eval::Array; /// Ensure that a condition is fulfilled. @@ -253,12 +254,22 @@ pub fn range(_: &mut Context, args: &mut Args) -> TypResult<Value> { /// Convert a string to lowercase. pub fn lower(_: &mut Context, args: &mut Args) -> TypResult<Value> { - Ok(args.expect::<EcoString>("string")?.to_lowercase().into()) + case(Case::Lower, args) } /// Convert a string to uppercase. pub fn upper(_: &mut Context, args: &mut Args) -> TypResult<Value> { - Ok(args.expect::<EcoString>("string")?.to_uppercase().into()) + case(Case::Upper, args) +} + +/// Change the case of a string or template. +fn case(case: Case, args: &mut Args) -> TypResult<Value> { + let Spanned { v, span } = args.expect("string or template")?; + Ok(match v { + Value::Str(v) => Value::Str(case.apply(&v).into()), + Value::Template(v) => Value::Template(v.styled(TextNode::CASE, Some(case))), + v => bail!(span, "expected string or template, found {}", v.type_name()), + }) } /// The length of a string, an array or a dictionary. |
