Lower and upper on templates

This commit is contained in:
Laurenz 2022-02-24 19:56:01 +01:00
parent ecd2bca606
commit efde5cac88
4 changed files with 50 additions and 7 deletions

View File

@ -114,8 +114,8 @@ impl Template {
if let Some((_, map)) = Arc::get_mut(styled) {
if !map.has_scoped() {
map.set(key, value);
return self;
}
return self;
}
}

View File

@ -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,

View File

@ -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.

View File

@ -2,7 +2,8 @@
---
// Test the `upper`, `lower`, and number formatting functions.
#upper("Abc 8 def")
#upper("Abc 8")
#upper[def]
#lower("SCREAMING MUST BE SILENCED in " + roman(1672) + " years")
@ -14,6 +15,10 @@
parbreak()
}
---
// Error: 8-9 expected string or template, found integer
#upper(1)
---
// Error: 9-11 must be at least zero
#symbol(-1)