summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-24 19:56:01 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-24 19:56:01 +0100
commitefde5cac88078f10485f715be66a27efba2f23d8 (patch)
tree6d8d8d778e88b317f0e85a8815f887757a445e36 /src/library
parentecd2bca606c0533ec6426b03fc216df256d43c3f (diff)
Lower and upper on templates
Diffstat (limited to 'src/library')
-rw-r--r--src/library/text.rs33
-rw-r--r--src/library/utility.rs15
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.