mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Make clippy happier and remove Str
This commit is contained in:
parent
8a38899c98
commit
63c274e7f6
@ -70,7 +70,7 @@ impl Array {
|
|||||||
|
|
||||||
/// Clear the array.
|
/// Clear the array.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if Rc::strong_count(&mut self.0) == 1 {
|
if Rc::strong_count(&self.0) == 1 {
|
||||||
Rc::make_mut(&mut self.0).clear();
|
Rc::make_mut(&mut self.0).clear();
|
||||||
} else {
|
} else {
|
||||||
*self = Self::new();
|
*self = Self::new();
|
||||||
|
@ -4,9 +4,9 @@ use std::iter::FromIterator;
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Str, Value};
|
use super::Value;
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::util::RcExt;
|
use crate::util::{EcoString, RcExt};
|
||||||
|
|
||||||
/// Create a new [`Dict`] from key-value pairs.
|
/// Create a new [`Dict`] from key-value pairs.
|
||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
@ -21,7 +21,7 @@ macro_rules! dict {
|
|||||||
|
|
||||||
/// A dictionary from strings to values with clone-on-write value semantics.
|
/// A dictionary from strings to values with clone-on-write value semantics.
|
||||||
#[derive(Default, Clone, PartialEq)]
|
#[derive(Default, Clone, PartialEq)]
|
||||||
pub struct Dict(Rc<BTreeMap<Str, Value>>);
|
pub struct Dict(Rc<BTreeMap<EcoString, Value>>);
|
||||||
|
|
||||||
impl Dict {
|
impl Dict {
|
||||||
/// Create a new, empty dictionary.
|
/// Create a new, empty dictionary.
|
||||||
@ -30,7 +30,7 @@ impl Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new dictionary from a mapping of strings to values.
|
/// Create a new dictionary from a mapping of strings to values.
|
||||||
pub fn from_map(map: BTreeMap<Str, Value>) -> Self {
|
pub fn from_map(map: BTreeMap<EcoString, Value>) -> Self {
|
||||||
Self(Rc::new(map))
|
Self(Rc::new(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ impl Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow the value the given `key` maps to.
|
/// Borrow the value the given `key` maps to.
|
||||||
pub fn get(&self, key: Str) -> StrResult<&Value> {
|
pub fn get(&self, key: EcoString) -> StrResult<&Value> {
|
||||||
self.0.get(&key).ok_or_else(|| missing_key(&key))
|
self.0.get(&key).ok_or_else(|| missing_key(&key))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,18 +53,18 @@ impl Dict {
|
|||||||
///
|
///
|
||||||
/// This inserts the key with [`None`](Value::None) as the value if not
|
/// This inserts the key with [`None`](Value::None) as the value if not
|
||||||
/// present so far.
|
/// present so far.
|
||||||
pub fn get_mut(&mut self, key: Str) -> &mut Value {
|
pub fn get_mut(&mut self, key: EcoString) -> &mut Value {
|
||||||
Rc::make_mut(&mut self.0).entry(key.into()).or_default()
|
Rc::make_mut(&mut self.0).entry(key).or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a mapping from the given `key` to the given `value`.
|
/// Insert a mapping from the given `key` to the given `value`.
|
||||||
pub fn insert(&mut self, key: Str, value: Value) {
|
pub fn insert(&mut self, key: EcoString, value: Value) {
|
||||||
Rc::make_mut(&mut self.0).insert(key.into(), value);
|
Rc::make_mut(&mut self.0).insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the dictionary.
|
/// Clear the dictionary.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if Rc::strong_count(&mut self.0) == 1 {
|
if Rc::strong_count(&self.0) == 1 {
|
||||||
Rc::make_mut(&mut self.0).clear();
|
Rc::make_mut(&mut self.0).clear();
|
||||||
} else {
|
} else {
|
||||||
*self = Self::new();
|
*self = Self::new();
|
||||||
@ -72,14 +72,14 @@ impl Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over pairs of references to the contained keys and values.
|
/// Iterate over pairs of references to the contained keys and values.
|
||||||
pub fn iter(&self) -> std::collections::btree_map::Iter<Str, Value> {
|
pub fn iter(&self) -> std::collections::btree_map::Iter<EcoString, Value> {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The missing key access error message.
|
/// The missing key access error message.
|
||||||
#[cold]
|
#[cold]
|
||||||
fn missing_key(key: &Str) -> String {
|
fn missing_key(key: &EcoString) -> String {
|
||||||
format!("dictionary does not contain key: {:?}", key)
|
format!("dictionary does not contain key: {:?}", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,21 +119,21 @@ impl AddAssign for Dict {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extend<(Str, Value)> for Dict {
|
impl Extend<(EcoString, Value)> for Dict {
|
||||||
fn extend<T: IntoIterator<Item = (Str, Value)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (EcoString, Value)>>(&mut self, iter: T) {
|
||||||
Rc::make_mut(&mut self.0).extend(iter);
|
Rc::make_mut(&mut self.0).extend(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<(Str, Value)> for Dict {
|
impl FromIterator<(EcoString, Value)> for Dict {
|
||||||
fn from_iter<T: IntoIterator<Item = (Str, Value)>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = (EcoString, Value)>>(iter: T) -> Self {
|
||||||
Self(Rc::new(iter.into_iter().collect()))
|
Self(Rc::new(iter.into_iter().collect()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Dict {
|
impl IntoIterator for Dict {
|
||||||
type Item = (Str, Value);
|
type Item = (EcoString, Value);
|
||||||
type IntoIter = std::collections::btree_map::IntoIter<Str, Value>;
|
type IntoIter = std::collections::btree_map::IntoIter<EcoString, Value>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Rc::take(self.0).into_iter()
|
Rc::take(self.0).into_iter()
|
||||||
@ -141,8 +141,8 @@ impl IntoIterator for Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Dict {
|
impl<'a> IntoIterator for &'a Dict {
|
||||||
type Item = (&'a Str, &'a Value);
|
type Item = (&'a EcoString, &'a Value);
|
||||||
type IntoIter = std::collections::btree_map::Iter<'a, Str, Value>;
|
type IntoIter = std::collections::btree_map::Iter<'a, EcoString, Value>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.iter()
|
self.iter()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Cast, EvalContext, Str, Value};
|
use super::{Cast, EvalContext, Value};
|
||||||
use crate::diag::{At, TypResult};
|
use crate::diag::{At, TypResult};
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
@ -52,7 +52,10 @@ impl Debug for Function {
|
|||||||
impl PartialEq for Function {
|
impl PartialEq for Function {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
// We cast to thin pointers for comparison.
|
// We cast to thin pointers for comparison.
|
||||||
Rc::as_ptr(&self.0) as *const () == Rc::as_ptr(&other.0) as *const ()
|
std::ptr::eq(
|
||||||
|
Rc::as_ptr(&self.0) as *const (),
|
||||||
|
Rc::as_ptr(&other.0) as *const (),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +74,7 @@ pub struct Arg {
|
|||||||
/// The span of the whole argument.
|
/// The span of the whole argument.
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// The name of the argument (`None` for positional arguments).
|
/// The name of the argument (`None` for positional arguments).
|
||||||
pub name: Option<Str>,
|
pub name: Option<EcoString>,
|
||||||
/// The value of the argument.
|
/// The value of the argument.
|
||||||
pub value: Spanned<Value>,
|
pub value: Spanned<Value>,
|
||||||
}
|
}
|
||||||
@ -173,7 +176,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reinterpret these arguments as actually being a dictionary key.
|
/// Reinterpret these arguments as actually being a dictionary key.
|
||||||
pub fn into_key(self) -> TypResult<Str> {
|
pub fn into_key(self) -> TypResult<EcoString> {
|
||||||
self.into_castable("key")
|
self.into_castable("key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@ mod array;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod dict;
|
mod dict;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod str;
|
|
||||||
#[macro_use]
|
|
||||||
mod value;
|
mod value;
|
||||||
mod capture;
|
mod capture;
|
||||||
mod function;
|
mod function;
|
||||||
@ -15,7 +13,6 @@ mod scope;
|
|||||||
mod template;
|
mod template;
|
||||||
mod walk;
|
mod walk;
|
||||||
|
|
||||||
pub use self::str::*;
|
|
||||||
pub use array::*;
|
pub use array::*;
|
||||||
pub use capture::*;
|
pub use capture::*;
|
||||||
pub use dict::*;
|
pub use dict::*;
|
||||||
@ -31,6 +28,8 @@ use std::io;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult};
|
use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult};
|
||||||
use crate::geom::{Angle, Fractional, Length, Relative};
|
use crate::geom::{Angle, Fractional, Length, Relative};
|
||||||
use crate::image::ImageStore;
|
use crate::image::ImageStore;
|
||||||
@ -38,7 +37,7 @@ use crate::loading::Loader;
|
|||||||
use crate::source::{SourceId, SourceStore};
|
use crate::source::{SourceId, SourceStore};
|
||||||
use crate::syntax::ast::*;
|
use crate::syntax::ast::*;
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::util::RefMutExt;
|
use crate::util::{EcoString, RefMutExt};
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
/// Evaluate a parsed source file into a module.
|
/// Evaluate a parsed source file into a module.
|
||||||
@ -210,7 +209,7 @@ impl Eval for Lit {
|
|||||||
LitKind::Angle(v, unit) => Value::Angle(Angle::with_unit(v, unit)),
|
LitKind::Angle(v, unit) => Value::Angle(Angle::with_unit(v, unit)),
|
||||||
LitKind::Percent(v) => Value::Relative(Relative::new(v / 100.0)),
|
LitKind::Percent(v) => Value::Relative(Relative::new(v / 100.0)),
|
||||||
LitKind::Fractional(v) => Value::Fractional(Fractional::new(v)),
|
LitKind::Fractional(v) => Value::Fractional(Fractional::new(v)),
|
||||||
LitKind::Str(ref v) => Value::Str(v.into()),
|
LitKind::Str(ref v) => Value::Str(v.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,7 +238,7 @@ impl Eval for DictExpr {
|
|||||||
|
|
||||||
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
||||||
self.items()
|
self.items()
|
||||||
.map(|x| Ok((x.name().take().into(), x.expr().eval(ctx)?)))
|
.map(|x| Ok((x.name().take(), x.expr().eval(ctx)?)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,7 +400,7 @@ impl Eval for CallArgs {
|
|||||||
CallArg::Named(named) => {
|
CallArg::Named(named) => {
|
||||||
items.push(Arg {
|
items.push(Arg {
|
||||||
span,
|
span,
|
||||||
name: Some(named.name().take().into()),
|
name: Some(named.name().take()),
|
||||||
value: Spanned::new(named.expr().eval(ctx)?, named.expr().span()),
|
value: Spanned::new(named.expr().eval(ctx)?, named.expr().span()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -600,7 +599,7 @@ impl Eval for ForExpr {
|
|||||||
|
|
||||||
match (key, value, iter) {
|
match (key, value, iter) {
|
||||||
(None, v, Value::Str(string)) => {
|
(None, v, Value::Str(string)) => {
|
||||||
iter!(for (v => value) in string.iter());
|
iter!(for (v => value) in string.graphemes(true));
|
||||||
}
|
}
|
||||||
(None, v, Value::Array(array)) => {
|
(None, v, Value::Array(array)) => {
|
||||||
iter!(for (v => value) in array.into_iter());
|
iter!(for (v => value) in array.into_iter());
|
||||||
@ -629,7 +628,7 @@ impl Eval for ImportExpr {
|
|||||||
|
|
||||||
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
let resolved = path.eval(ctx)?.cast::<Str>().at(path.span())?;
|
let resolved = path.eval(ctx)?.cast::<EcoString>().at(path.span())?;
|
||||||
let file = ctx.import(&resolved, path.span())?;
|
let file = ctx.import(&resolved, path.span())?;
|
||||||
let module = &ctx.modules[&file];
|
let module = &ctx.modules[&file];
|
||||||
|
|
||||||
@ -659,7 +658,7 @@ impl Eval for IncludeExpr {
|
|||||||
|
|
||||||
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
let resolved = path.eval(ctx)?.cast::<Str>().at(path.span())?;
|
let resolved = path.eval(ctx)?.cast::<EcoString>().at(path.span())?;
|
||||||
let file = ctx.import(&resolved, path.span())?;
|
let file = ctx.import(&resolved, path.span())?;
|
||||||
let module = &ctx.modules[&file];
|
let module = &ctx.modules[&file];
|
||||||
Ok(Value::Template(module.template.clone()))
|
Ok(Value::Template(module.template.clone()))
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
|
use crate::util::EcoString;
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
||||||
/// Bail with a type mismatch error.
|
/// Bail with a type mismatch error.
|
||||||
@ -150,8 +152,8 @@ pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
(Fractional(a), Float(b)) => Fractional(a * b),
|
(Fractional(a), Float(b)) => Fractional(a * b),
|
||||||
(Int(a), Fractional(b)) => Fractional(a as f64 * b),
|
(Int(a), Fractional(b)) => Fractional(a as f64 * b),
|
||||||
|
|
||||||
(Str(a), Int(b)) => Str(a.repeat(b)?),
|
(Str(a), Int(b)) => Str(repeat_str(a, b)?),
|
||||||
(Int(a), Str(b)) => Str(b.repeat(a)?),
|
(Int(a), Str(b)) => Str(repeat_str(b, a)?),
|
||||||
(Array(a), Int(b)) => Array(a.repeat(b)?),
|
(Array(a), Int(b)) => Array(a.repeat(b)?),
|
||||||
(Int(a), Array(b)) => Array(b.repeat(a)?),
|
(Int(a), Array(b)) => Array(b.repeat(a)?),
|
||||||
(Template(a), Int(b)) => Template(a.repeat(b)?),
|
(Template(a), Int(b)) => Template(a.repeat(b)?),
|
||||||
@ -161,6 +163,16 @@ pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Repeat a string a number of times.
|
||||||
|
fn repeat_str(string: EcoString, n: i64) -> StrResult<EcoString> {
|
||||||
|
let n = usize::try_from(n)
|
||||||
|
.ok()
|
||||||
|
.and_then(|n| string.len().checked_mul(n).map(|_| n))
|
||||||
|
.ok_or_else(|| format!("cannot repeat this string {} times", n))?;
|
||||||
|
|
||||||
|
Ok(string.repeat(n))
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the quotient of two values.
|
/// Compute the quotient of two values.
|
||||||
pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> {
|
pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> {
|
||||||
Ok(match (lhs, rhs) {
|
Ok(match (lhs, rhs) {
|
||||||
|
164
src/eval/str.rs
164
src/eval/str.rs
@ -1,164 +0,0 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::fmt::{self, Debug, Formatter, Write};
|
|
||||||
use std::ops::{Add, AddAssign, Deref};
|
|
||||||
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
|
|
||||||
use crate::diag::StrResult;
|
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// Create a new [`Str`] from a format string.
|
|
||||||
macro_rules! format_str {
|
|
||||||
($($tts:tt)*) => {{
|
|
||||||
use std::fmt::Write;
|
|
||||||
let mut s = $crate::eval::Str::new();
|
|
||||||
write!(s, $($tts)*).unwrap();
|
|
||||||
s
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A string value with inline storage and clone-on-write semantics.
|
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
||||||
pub struct Str(EcoString);
|
|
||||||
|
|
||||||
impl Str {
|
|
||||||
/// Create a new, empty string.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the string is empty.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The length of the string in bytes.
|
|
||||||
pub fn len(&self) -> i64 {
|
|
||||||
self.0.len() as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow this as a string slice.
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
self.0.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an iterator over the grapheme clusters as strings.
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Str> + '_ {
|
|
||||||
self.graphemes(true).map(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Repeat this string `n` times.
|
|
||||||
pub fn repeat(&self, n: i64) -> StrResult<Self> {
|
|
||||||
let n = usize::try_from(n)
|
|
||||||
.ok()
|
|
||||||
.and_then(|n| self.0.len().checked_mul(n).map(|_| n))
|
|
||||||
.ok_or_else(|| format!("cannot repeat this string {} times", n))?;
|
|
||||||
|
|
||||||
Ok(self.0.repeat(n).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Str {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
fn deref(&self) -> &str {
|
|
||||||
self.0.deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Str {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.write_char('"')?;
|
|
||||||
for c in self.chars() {
|
|
||||||
match c {
|
|
||||||
'\\' => f.write_str(r"\\")?,
|
|
||||||
'"' => f.write_str(r#"\""#)?,
|
|
||||||
'\n' => f.write_str(r"\n")?,
|
|
||||||
'\r' => f.write_str(r"\r")?,
|
|
||||||
'\t' => f.write_str(r"\t")?,
|
|
||||||
_ => f.write_char(c)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.write_char('"')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Str {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(mut self, rhs: Self) -> Self::Output {
|
|
||||||
self += rhs;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for Str {
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
self.0.push_str(rhs.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for Str {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
self.0.write_str(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
|
||||||
self.0.write_char(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for Str {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Borrow<str> for Str {
|
|
||||||
fn borrow(&self) -> &str {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<char> for Str {
|
|
||||||
fn from(c: char) -> Self {
|
|
||||||
Self(c.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Str {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Self(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Str {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EcoString> for Str {
|
|
||||||
fn from(s: EcoString) -> Self {
|
|
||||||
Self(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&EcoString> for Str {
|
|
||||||
fn from(s: &EcoString) -> Self {
|
|
||||||
Self(s.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Str> for EcoString {
|
|
||||||
fn from(s: Str) -> Self {
|
|
||||||
s.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Str> for EcoString {
|
|
||||||
fn from(s: &Str) -> Self {
|
|
||||||
s.0.clone()
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ use std::mem;
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::Str;
|
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
|
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
|
||||||
use crate::layout::{BlockLevel, BlockNode, InlineLevel, InlineNode, PageNode};
|
use crate::layout::{BlockLevel, BlockNode, InlineLevel, InlineNode, PageNode};
|
||||||
@ -214,20 +213,20 @@ impl AddAssign for Template {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Str> for Template {
|
impl Add<EcoString> for Template {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(mut self, rhs: Str) -> Self::Output {
|
fn add(mut self, rhs: EcoString) -> Self::Output {
|
||||||
Rc::make_mut(&mut self.0).push(TemplateNode::Text(rhs.into()));
|
Rc::make_mut(&mut self.0).push(TemplateNode::Text(rhs));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Template> for Str {
|
impl Add<Template> for EcoString {
|
||||||
type Output = Template;
|
type Output = Template;
|
||||||
|
|
||||||
fn add(self, mut rhs: Template) -> Self::Output {
|
fn add(self, mut rhs: Template) -> Self::Output {
|
||||||
Rc::make_mut(&mut rhs.0).insert(0, TemplateNode::Text(self.into()));
|
Rc::make_mut(&mut rhs.0).insert(0, TemplateNode::Text(self));
|
||||||
rhs
|
rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,7 +490,7 @@ impl ParBuilder {
|
|||||||
self.children.last_mut()
|
self.children.last_mut()
|
||||||
{
|
{
|
||||||
if prev_align == curr_align && Rc::ptr_eq(prev_props, curr_props) {
|
if prev_align == curr_align && Rc::ptr_eq(prev_props, curr_props) {
|
||||||
prev_text.push_str(&curr_text);
|
prev_text.push_str(curr_text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::cmp::Ordering;
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{ops, Array, Dict, Function, Str, Template};
|
use super::{ops, Array, Dict, Function, Template};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
|
use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
@ -35,7 +35,7 @@ pub enum Value {
|
|||||||
/// A color value: `#f79143ff`.
|
/// A color value: `#f79143ff`.
|
||||||
Color(Color),
|
Color(Color),
|
||||||
/// A string: `"string"`.
|
/// A string: `"string"`.
|
||||||
Str(Str),
|
Str(EcoString),
|
||||||
/// An array of values: `(1, "hi", 12cm)`.
|
/// An array of values: `(1, "hi", 12cm)`.
|
||||||
Array(Array),
|
Array(Array),
|
||||||
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
|
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
|
||||||
@ -63,7 +63,7 @@ impl Value {
|
|||||||
Self::Linear(_) => Linear::TYPE_NAME,
|
Self::Linear(_) => Linear::TYPE_NAME,
|
||||||
Self::Fractional(_) => Fractional::TYPE_NAME,
|
Self::Fractional(_) => Fractional::TYPE_NAME,
|
||||||
Self::Color(_) => Color::TYPE_NAME,
|
Self::Color(_) => Color::TYPE_NAME,
|
||||||
Self::Str(_) => Str::TYPE_NAME,
|
Self::Str(_) => EcoString::TYPE_NAME,
|
||||||
Self::Array(_) => Array::TYPE_NAME,
|
Self::Array(_) => Array::TYPE_NAME,
|
||||||
Self::Dict(_) => Dict::TYPE_NAME,
|
Self::Dict(_) => Dict::TYPE_NAME,
|
||||||
Self::Template(_) => Template::TYPE_NAME,
|
Self::Template(_) => Template::TYPE_NAME,
|
||||||
@ -81,8 +81,8 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the debug representation of the value.
|
/// Return the debug representation of the value.
|
||||||
pub fn repr(&self) -> Str {
|
pub fn repr(&self) -> EcoString {
|
||||||
format_str!("{:?}", self)
|
format_eco!("{:?}", self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Join the value with another value.
|
/// Join the value with another value.
|
||||||
@ -163,17 +163,12 @@ impl From<String> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EcoString> for Value {
|
|
||||||
fn from(v: EcoString) -> Self {
|
|
||||||
Self::Str(v.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Dynamic> for Value {
|
impl From<Dynamic> for Value {
|
||||||
fn from(v: Dynamic) -> Self {
|
fn from(v: Dynamic) -> Self {
|
||||||
Self::Dyn(v)
|
Self::Dyn(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dynamic value.
|
/// A dynamic value.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Dynamic(Rc<dyn Bounds>);
|
pub struct Dynamic(Rc<dyn Bounds>);
|
||||||
@ -338,7 +333,8 @@ macro_rules! dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
$type: <Self as $crate::eval::Type>::TYPE_NAME,
|
$type,
|
||||||
|
Expected: <Self as $crate::eval::Type>::TYPE_NAME,
|
||||||
$($tts)*
|
$($tts)*
|
||||||
@this: Self => this.clone(),
|
@this: Self => this.clone(),
|
||||||
}
|
}
|
||||||
@ -354,8 +350,8 @@ macro_rules! dynamic {
|
|||||||
/// Make a type castable from a value.
|
/// Make a type castable from a value.
|
||||||
macro_rules! castable {
|
macro_rules! castable {
|
||||||
(
|
(
|
||||||
$type:ty:
|
$type:ty,
|
||||||
$expected:expr,
|
Expected: $expected:expr,
|
||||||
$($pattern:pat => $out:expr,)*
|
$($pattern:pat => $out:expr,)*
|
||||||
$(@$dyn_in:ident: $dyn_type:ty => $dyn_out:expr,)*
|
$(@$dyn_in:ident: $dyn_type:ty => $dyn_out:expr,)*
|
||||||
) => {
|
) => {
|
||||||
@ -398,7 +394,7 @@ primitive! { Relative: "relative", Relative }
|
|||||||
primitive! { Linear: "linear", Linear, Length(v) => v.into(), Relative(v) => v.into() }
|
primitive! { Linear: "linear", Linear, Length(v) => v.into(), Relative(v) => v.into() }
|
||||||
primitive! { Fractional: "fractional", Fractional }
|
primitive! { Fractional: "fractional", Fractional }
|
||||||
primitive! { Color: "color", Color }
|
primitive! { Color: "color", Color }
|
||||||
primitive! { Str: "string", Str }
|
primitive! { EcoString: "string", Str }
|
||||||
primitive! { Array: "array", Array }
|
primitive! { Array: "array", Array }
|
||||||
primitive! { Dict: "dictionary", Dict }
|
primitive! { Dict: "dictionary", Dict }
|
||||||
primitive! { Template: "template", Template }
|
primitive! { Template: "template", Template }
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Eval, EvalContext, Str, Template, Value};
|
use super::{Eval, EvalContext, Template, Value};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::TypResult;
|
||||||
use crate::geom::Spec;
|
use crate::geom::Spec;
|
||||||
use crate::layout::BlockLevel;
|
use crate::layout::BlockLevel;
|
||||||
use crate::library::{GridNode, ParChild, ParNode, TrackSizing};
|
use crate::library::{GridNode, ParChild, ParNode, TrackSizing};
|
||||||
use crate::syntax::ast::*;
|
use crate::syntax::ast::*;
|
||||||
use crate::util::BoolExt;
|
use crate::util::{BoolExt, EcoString};
|
||||||
|
|
||||||
/// Walk markup, filling the currently built template.
|
/// Walk markup, filling the currently built template.
|
||||||
pub trait Walk {
|
pub trait Walk {
|
||||||
@ -39,8 +39,8 @@ impl Walk for MarkupNode {
|
|||||||
Self::Enum(enum_) => enum_.walk(ctx)?,
|
Self::Enum(enum_) => enum_.walk(ctx)?,
|
||||||
Self::Expr(expr) => match expr.eval(ctx)? {
|
Self::Expr(expr) => match expr.eval(ctx)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Int(v) => ctx.template.text(format_str!("{}", v)),
|
Value::Int(v) => ctx.template.text(format_eco!("{}", v)),
|
||||||
Value::Float(v) => ctx.template.text(format_str!("{}", v)),
|
Value::Float(v) => ctx.template.text(format_eco!("{}", v)),
|
||||||
Value::Str(v) => ctx.template.text(v),
|
Value::Str(v) => ctx.template.text(v),
|
||||||
Value::Template(v) => ctx.template += v,
|
Value::Template(v) => ctx.template += v,
|
||||||
// For values which can't be shown "naturally", we print the
|
// For values which can't be shown "naturally", we print the
|
||||||
@ -108,7 +108,7 @@ impl Walk for HeadingNode {
|
|||||||
impl Walk for ListNode {
|
impl Walk for ListNode {
|
||||||
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
|
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
|
||||||
let body = self.body().eval(ctx)?;
|
let body = self.body().eval(ctx)?;
|
||||||
walk_item(ctx, Str::from('•'), body);
|
walk_item(ctx, EcoString::from('•'), body);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,19 +116,19 @@ impl Walk for ListNode {
|
|||||||
impl Walk for EnumNode {
|
impl Walk for EnumNode {
|
||||||
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
|
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
|
||||||
let body = self.body().eval(ctx)?;
|
let body = self.body().eval(ctx)?;
|
||||||
let label = format_str!("{}.", self.number().unwrap_or(1));
|
let label = format_eco!("{}.", self.number().unwrap_or(1));
|
||||||
walk_item(ctx, label, body);
|
walk_item(ctx, label, body);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) {
|
fn walk_item(ctx: &mut EvalContext, label: EcoString, body: Template) {
|
||||||
ctx.template += Template::from_block(move |style| {
|
ctx.template += Template::from_block(move |style| {
|
||||||
let label = ParNode {
|
let label = ParNode {
|
||||||
dir: style.dir,
|
dir: style.dir,
|
||||||
leading: style.leading(),
|
leading: style.leading(),
|
||||||
children: vec![ParChild::Text(
|
children: vec![ParChild::Text(
|
||||||
(&label).into(),
|
label.clone(),
|
||||||
style.aligns.inline,
|
style.aligns.inline,
|
||||||
Rc::clone(&style.text),
|
Rc::clone(&style.text),
|
||||||
)],
|
)],
|
||||||
@ -138,7 +138,7 @@ fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) {
|
|||||||
GridNode {
|
GridNode {
|
||||||
tracks: Spec::new(vec![TrackSizing::Auto; 2], vec![]),
|
tracks: Spec::new(vec![TrackSizing::Auto; 2], vec![]),
|
||||||
gutter: Spec::new(vec![TrackSizing::Linear(spacing.into())], vec![]),
|
gutter: Spec::new(vec![TrackSizing::Linear(spacing.into())], vec![]),
|
||||||
children: vec![label.pack(), body.to_stack(&style).pack()],
|
children: vec![label.pack(), body.to_stack(style).pack()],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
let mut resources = pages.resources();
|
let mut resources = pages.resources();
|
||||||
let mut fonts = resources.fonts();
|
let mut fonts = resources.fonts();
|
||||||
for (refs, f) in self.refs.fonts().zip(self.font_map.pdf_indices()) {
|
for (refs, f) in self.refs.fonts().zip(self.font_map.pdf_indices()) {
|
||||||
let name = format!("F{}", f);
|
let name = format_eco!("F{}", f);
|
||||||
fonts.pair(Name(name.as_bytes()), refs.type0_font);
|
fonts.pair(Name(name.as_bytes()), refs.type0_font);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
|
|
||||||
let mut images = resources.x_objects();
|
let mut images = resources.x_objects();
|
||||||
for (id, im) in self.refs.images().zip(self.image_map.pdf_indices()) {
|
for (id, im) in self.refs.images().zip(self.image_map.pdf_indices()) {
|
||||||
let name = format!("Im{}", im);
|
let name = format_eco!("Im{}", im);
|
||||||
images.pair(Name(name.as_bytes()), id);
|
images.pair(Name(name.as_bytes()), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
|
|
||||||
fn write_pages(&mut self) {
|
fn write_pages(&mut self) {
|
||||||
for (id, page) in self.refs.contents().zip(self.frames) {
|
for (id, page) in self.refs.contents().zip(self.frames) {
|
||||||
self.write_page(id, &page);
|
self.write_page(id, page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
face_id = Some(text.face_id);
|
face_id = Some(text.face_id);
|
||||||
size = text.size;
|
size = text.size;
|
||||||
|
|
||||||
let name = format!("F{}", self.font_map.map(text.face_id));
|
let name = format_eco!("F{}", self.font_map.map(text.face_id));
|
||||||
content.set_font(Name(name.as_bytes()), size.to_pt() as f32);
|
content.set_font(Name(name.as_bytes()), size.to_pt() as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,7 +541,7 @@ fn encode_image(img: &Image) -> ImageResult<(Vec<u8>, Filter, ColorSpace)> {
|
|||||||
|
|
||||||
// 8-bit gray PNG.
|
// 8-bit gray PNG.
|
||||||
(ImageFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
(ImageFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
||||||
let data = deflate(&luma.as_raw());
|
let data = deflate(luma.as_raw());
|
||||||
(data, Filter::FlateDecode, ColorSpace::DeviceGray)
|
(data, Filter::FlateDecode, ColorSpace::DeviceGray)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ struct Subsetter<'a> {
|
|||||||
impl<'a> Subsetter<'a> {
|
impl<'a> Subsetter<'a> {
|
||||||
/// Parse the font header and create a new subsetter.
|
/// Parse the font header and create a new subsetter.
|
||||||
fn new(data: &'a [u8], index: u32, glyphs: &'a HashSet<u16>) -> Option<Self> {
|
fn new(data: &'a [u8], index: u32, glyphs: &'a HashSet<u16>) -> Option<Self> {
|
||||||
let mut s = Stream::new(&data);
|
let mut s = Stream::new(data);
|
||||||
|
|
||||||
let mut magic = s.read::<Magic>()?;
|
let mut magic = s.read::<Magic>()?;
|
||||||
if magic == Magic::Collection {
|
if magic == Magic::Collection {
|
||||||
@ -46,7 +46,7 @@ impl<'a> Subsetter<'a> {
|
|||||||
let offsets = s.read_array32::<Offset32>(num_faces)?;
|
let offsets = s.read_array32::<Offset32>(num_faces)?;
|
||||||
let offset = offsets.get(index)?.to_usize();
|
let offset = offsets.get(index)?.to_usize();
|
||||||
|
|
||||||
s = Stream::new_at(&data, offset)?;
|
s = Stream::new_at(data, offset)?;
|
||||||
magic = s.read::<Magic>()?;
|
magic = s.read::<Magic>()?;
|
||||||
if magic == Magic::Collection {
|
if magic == Magic::Collection {
|
||||||
return None;
|
return None;
|
||||||
@ -117,7 +117,7 @@ impl<'a> Subsetter<'a> {
|
|||||||
let len = data.len();
|
let len = data.len();
|
||||||
w.write(TableRecord {
|
w.write(TableRecord {
|
||||||
tag: *tag,
|
tag: *tag,
|
||||||
checksum: checksum(&data),
|
checksum: checksum(data),
|
||||||
offset: offset as u32,
|
offset: offset as u32,
|
||||||
length: len as u32,
|
length: len as u32,
|
||||||
});
|
});
|
||||||
|
24
src/font.rs
24
src/font.rs
@ -317,30 +317,6 @@ pub enum VerticalFontMetric {
|
|||||||
Linear(Linear),
|
Linear(Linear),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic or named font family.
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum FontFamily {
|
|
||||||
/// A family that has "serifs", small strokes attached to letters.
|
|
||||||
Serif,
|
|
||||||
/// A family in which glyphs do not have "serifs", small attached strokes.
|
|
||||||
SansSerif,
|
|
||||||
/// A family in which (almost) all glyphs are of equal width.
|
|
||||||
Monospace,
|
|
||||||
/// A specific family with a name.
|
|
||||||
Named(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for FontFamily {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.pad(match self {
|
|
||||||
Self::Serif => "serif",
|
|
||||||
Self::SansSerif => "sans-serif",
|
|
||||||
Self::Monospace => "monospace",
|
|
||||||
Self::Named(s) => s,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Properties of a single font face.
|
/// Properties of a single font face.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct FaceInfo {
|
pub struct FaceInfo {
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
//! [layouted]: layout::layout
|
//! [layouted]: layout::layout
|
||||||
//! [PDF]: export::pdf
|
//! [PDF]: export::pdf
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod util;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod diag;
|
pub mod diag;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -43,7 +45,6 @@ pub mod parse;
|
|||||||
pub mod source;
|
pub mod source;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
pub mod util;
|
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -182,7 +183,7 @@ impl ContextBuilder {
|
|||||||
loader,
|
loader,
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
layouts: LayoutCache::new(self.policy, self.max_size),
|
layouts: LayoutCache::new(self.policy, self.max_size),
|
||||||
std: self.std.unwrap_or(library::new()),
|
std: self.std.unwrap_or_else(library::new),
|
||||||
style: self.style.unwrap_or_default(),
|
style: self.style.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,14 +36,14 @@ fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
|
|||||||
|
|
||||||
/// `link`: Typeset text as a link.
|
/// `link`: Typeset text as a link.
|
||||||
pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let url = args.expect::<Str>("url")?;
|
let url = args.expect::<EcoString>("url")?;
|
||||||
let body = args.find().unwrap_or_else(|| {
|
let body = args.find().unwrap_or_else(|| {
|
||||||
let mut template = Template::new();
|
let mut template = Template::new();
|
||||||
template.text(url.trim_start_matches("mailto:").trim_start_matches("tel:"));
|
template.text(url.trim_start_matches("mailto:").trim_start_matches("tel:"));
|
||||||
template
|
template
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Value::Template(body.decorate(Decoration::Link(url.into()))))
|
Ok(Value::Template(body.decorate(Decoration::Link(url))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A decoration for a frame.
|
/// A decoration for a frame.
|
||||||
|
@ -3,7 +3,8 @@ use super::prelude::*;
|
|||||||
/// `grid`: Arrange children into a grid.
|
/// `grid`: Arrange children into a grid.
|
||||||
pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
castable! {
|
castable! {
|
||||||
Vec<TrackSizing>: "integer or (auto, linear, fractional, or array thereof)",
|
Vec<TrackSizing>,
|
||||||
|
Expected: "integer or (auto, linear, fractional, or array thereof)",
|
||||||
Value::Auto => vec![TrackSizing::Auto],
|
Value::Auto => vec![TrackSizing::Auto],
|
||||||
Value::Length(v) => vec![TrackSizing::Linear(v.into())],
|
Value::Length(v) => vec![TrackSizing::Linear(v.into())],
|
||||||
Value::Relative(v) => vec![TrackSizing::Linear(v.into())],
|
Value::Relative(v) => vec![TrackSizing::Linear(v.into())],
|
||||||
@ -17,7 +18,8 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
TrackSizing: "auto, linear, or fractional",
|
TrackSizing,
|
||||||
|
Expected: "auto, linear, or fractional",
|
||||||
Value::Auto => Self::Auto,
|
Value::Auto => Self::Auto,
|
||||||
Value::Length(v) => Self::Linear(v.into()),
|
Value::Length(v) => Self::Linear(v.into()),
|
||||||
Value::Relative(v) => Self::Linear(v.into()),
|
Value::Relative(v) => Self::Linear(v.into()),
|
||||||
@ -43,10 +45,7 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
GridNode {
|
GridNode {
|
||||||
tracks: tracks.clone(),
|
tracks: tracks.clone(),
|
||||||
gutter: gutter.clone(),
|
gutter: gutter.clone(),
|
||||||
children: children
|
children: children.iter().map(|child| child.to_stack(style).pack()).collect(),
|
||||||
.iter()
|
|
||||||
.map(|child| child.to_stack(&style).pack())
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::image::ImageId;
|
|||||||
|
|
||||||
/// `image`: An image.
|
/// `image`: An image.
|
||||||
pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let path = args.expect::<Spanned<Str>>("path to image file")?;
|
let path = args.expect::<Spanned<EcoString>>("path to image file")?;
|
||||||
let width = args.named("width")?;
|
let width = args.named("width")?;
|
||||||
let height = args.named("height")?;
|
let height = args.named("height")?;
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ mod prelude {
|
|||||||
pub use std::rc::Rc;
|
pub use std::rc::Rc;
|
||||||
|
|
||||||
pub use crate::diag::{At, TypResult};
|
pub use crate::diag::{At, TypResult};
|
||||||
pub use crate::eval::{Args, EvalContext, Str, Template, Value};
|
pub use crate::eval::{Args, EvalContext, Template, Value};
|
||||||
pub use crate::frame::*;
|
pub use crate::frame::*;
|
||||||
pub use crate::geom::*;
|
pub use crate::geom::*;
|
||||||
pub use crate::layout::*;
|
pub use crate::layout::*;
|
||||||
pub use crate::syntax::{Span, Spanned};
|
pub use crate::syntax::{Span, Spanned};
|
||||||
pub use crate::util::OptionExt;
|
pub use crate::util::{EcoString, OptionExt};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::image::*;
|
pub use self::image::*;
|
||||||
@ -47,8 +47,8 @@ pub use transform::*;
|
|||||||
pub use utility::*;
|
pub use utility::*;
|
||||||
|
|
||||||
use crate::eval::{Scope, Value};
|
use crate::eval::{Scope, Value};
|
||||||
use crate::font::FontFamily;
|
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
|
use crate::style::FontFamily;
|
||||||
|
|
||||||
/// Construct a scope containing all standard library definitions.
|
/// Construct a scope containing all standard library definitions.
|
||||||
pub fn new() -> Scope {
|
pub fn new() -> Scope {
|
||||||
|
@ -19,7 +19,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
Ok(Value::Template(Template::from_block(move |style| {
|
Ok(Value::Template(Template::from_block(move |style| {
|
||||||
PadNode {
|
PadNode {
|
||||||
padding,
|
padding,
|
||||||
child: body.to_stack(&style).pack(),
|
child: body.to_stack(style).pack(),
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@ use crate::style::{Paper, PaperClass};
|
|||||||
/// `page`: Configure pages.
|
/// `page`: Configure pages.
|
||||||
pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
castable! {
|
castable! {
|
||||||
Paper: "string",
|
Paper,
|
||||||
|
Expected: "string",
|
||||||
Value::Str(string) => Paper::from_name(&string).ok_or("unknown paper")?,
|
Value::Str(string) => Paper::from_name(&string).ok_or("unknown paper")?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
/// `lang`: Configure the language.
|
/// `lang`: Configure the language.
|
||||||
pub fn lang(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn lang(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let iso = args.find::<Str>();
|
let iso = args.find::<EcoString>();
|
||||||
let dir = if let Some(dir) = args.named::<Spanned<Dir>>("dir")? {
|
let dir = if let Some(dir) = args.named::<Spanned<Dir>>("dir")? {
|
||||||
if dir.v.axis() == SpecAxis::Horizontal {
|
if dir.v.axis() == SpecAxis::Horizontal {
|
||||||
Some(dir.v)
|
Some(dir.v)
|
||||||
|
@ -24,7 +24,8 @@ pub enum Spacing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
Spacing: "linear or fractional",
|
Spacing,
|
||||||
|
Expected: "linear or fractional",
|
||||||
Value::Length(v) => Self::Linear(v.into()),
|
Value::Length(v) => Self::Linear(v.into()),
|
||||||
Value::Relative(v) => Self::Linear(v.into()),
|
Value::Relative(v) => Self::Linear(v.into()),
|
||||||
Value::Linear(v) => Self::Linear(v),
|
Value::Linear(v) => Self::Linear(v),
|
||||||
|
@ -11,7 +11,8 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
Child: "linear, fractional or template",
|
Child,
|
||||||
|
Expected: "linear, fractional or template",
|
||||||
Value::Length(v) => Self::Spacing(Spacing::Linear(v.into())),
|
Value::Length(v) => Self::Spacing(Spacing::Linear(v.into())),
|
||||||
Value::Relative(v) => Self::Spacing(Spacing::Linear(v.into())),
|
Value::Relative(v) => Self::Spacing(Spacing::Linear(v.into())),
|
||||||
Value::Linear(v) => Self::Spacing(Spacing::Linear(v)),
|
Value::Linear(v) => Self::Spacing(Spacing::Linear(v)),
|
||||||
|
@ -7,48 +7,42 @@ use ttf_parser::Tag;
|
|||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::font::{
|
use crate::font::{
|
||||||
Face, FaceId, FontFamily, FontStore, FontStretch, FontStyle, FontVariant, FontWeight,
|
Face, FaceId, FontStore, FontStretch, FontStyle, FontVariant, FontWeight,
|
||||||
VerticalFontMetric,
|
VerticalFontMetric,
|
||||||
};
|
};
|
||||||
use crate::geom::{Dir, Em, Length, Point, Size};
|
use crate::geom::{Dir, Em, Length, Point, Size};
|
||||||
use crate::style::{
|
use crate::style::{
|
||||||
FontFeatures, NumberPosition, NumberType, NumberWidth, Style, TextStyle,
|
FontFamily, FontFeatures, NumberPosition, NumberType, NumberWidth, Style,
|
||||||
|
StylisticSet, TextStyle,
|
||||||
};
|
};
|
||||||
use crate::util::SliceExt;
|
use crate::util::{EcoString, SliceExt};
|
||||||
|
|
||||||
/// `font`: Configure the font.
|
/// `font`: Configure the font.
|
||||||
pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
struct FontDef(Rc<Vec<FontFamily>>);
|
|
||||||
struct FamilyDef(Rc<Vec<String>>);
|
|
||||||
struct FeatureList(Vec<(Tag, u32)>);
|
|
||||||
struct StylisticSet(Option<u8>);
|
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FontDef: "font family or array of font families",
|
Vec<FontFamily>,
|
||||||
Value::Str(string) => Self(Rc::new(vec![FontFamily::Named(string.to_lowercase())])),
|
Expected: "string, generic family or array thereof",
|
||||||
Value::Array(values) => Self(Rc::new(
|
Value::Str(string) => vec![FontFamily::Named(string.to_lowercase())],
|
||||||
values
|
Value::Array(values) => {
|
||||||
.into_iter()
|
values.into_iter().filter_map(|v| v.cast().ok()).collect()
|
||||||
.filter_map(|v| v.cast().ok())
|
},
|
||||||
.collect()
|
@family: FontFamily => vec![family.clone()],
|
||||||
)),
|
|
||||||
@family: FontFamily => Self(Rc::new(vec![family.clone()])),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FamilyDef: "string or array of strings",
|
Vec<EcoString>,
|
||||||
Value::Str(string) => Self(Rc::new(vec![string.to_lowercase()])),
|
Expected: "string or array of strings",
|
||||||
Value::Array(values) => Self(Rc::new(
|
Value::Str(string) => vec![string.to_lowercase()],
|
||||||
values
|
Value::Array(values) => values
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| v.cast().ok())
|
.filter_map(|v| v.cast().ok())
|
||||||
.map(|string: Str| string.to_lowercase())
|
.map(|string: EcoString| string.to_lowercase())
|
||||||
.collect()
|
.collect(),
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FontStyle: "string",
|
FontStyle,
|
||||||
|
Expected: "string",
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"normal" => Self::Normal,
|
"normal" => Self::Normal,
|
||||||
"italic" => Self::Italic,
|
"italic" => Self::Italic,
|
||||||
@ -58,7 +52,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FontWeight: "integer or string",
|
FontWeight,
|
||||||
|
Expected: "integer or string",
|
||||||
Value::Int(v) => v.try_into().map_or(Self::BLACK, Self::from_number),
|
Value::Int(v) => v.try_into().map_or(Self::BLACK, Self::from_number),
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"thin" => Self::THIN,
|
"thin" => Self::THIN,
|
||||||
@ -75,12 +70,14 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FontStretch: "relative",
|
FontStretch,
|
||||||
|
Expected: "relative",
|
||||||
Value::Relative(v) => Self::from_ratio(v.get() as f32),
|
Value::Relative(v) => Self::from_ratio(v.get() as f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
VerticalFontMetric: "linear or string",
|
VerticalFontMetric,
|
||||||
|
Expected: "linear or string",
|
||||||
Value::Length(v) => Self::Linear(v.into()),
|
Value::Length(v) => Self::Linear(v.into()),
|
||||||
Value::Relative(v) => Self::Linear(v.into()),
|
Value::Relative(v) => Self::Linear(v.into()),
|
||||||
Value::Linear(v) => Self::Linear(v),
|
Value::Linear(v) => Self::Linear(v),
|
||||||
@ -95,7 +92,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
StylisticSet: "none or integer",
|
StylisticSet,
|
||||||
|
Expected: "none or integer",
|
||||||
Value::None => Self(None),
|
Value::None => Self(None),
|
||||||
Value::Int(v) => match v {
|
Value::Int(v) => match v {
|
||||||
1 ..= 20 => Self(Some(v as u8)),
|
1 ..= 20 => Self(Some(v as u8)),
|
||||||
@ -104,7 +102,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
NumberType: "auto or string",
|
NumberType,
|
||||||
|
Expected: "auto or string",
|
||||||
Value::Auto => Self::Auto,
|
Value::Auto => Self::Auto,
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"lining" => Self::Lining,
|
"lining" => Self::Lining,
|
||||||
@ -114,7 +113,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
NumberWidth: "auto or string",
|
NumberWidth,
|
||||||
|
Expected: "auto or string",
|
||||||
Value::Auto => Self::Auto,
|
Value::Auto => Self::Auto,
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"proportional" => Self::Proportional,
|
"proportional" => Self::Proportional,
|
||||||
@ -124,7 +124,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
NumberPosition: "string",
|
NumberPosition,
|
||||||
|
Expected: "string",
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"normal" => Self::Normal,
|
"normal" => Self::Normal,
|
||||||
"subscript" => Self::Subscript,
|
"subscript" => Self::Subscript,
|
||||||
@ -134,44 +135,39 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
FeatureList: "array of strings or dictionary mapping tags to integers",
|
Vec<(Tag, u32)>,
|
||||||
Value::Array(values) => Self(
|
Expected: "array of strings or dictionary mapping tags to integers",
|
||||||
values
|
Value::Array(values) => values
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| v.cast().ok())
|
.filter_map(|v| v.cast().ok())
|
||||||
.map(|string: Str| (Tag::from_bytes_lossy(string.as_bytes()), 1))
|
.map(|string: EcoString| (Tag::from_bytes_lossy(string.as_bytes()), 1))
|
||||||
.collect()
|
.collect(),
|
||||||
),
|
Value::Dict(values) => values
|
||||||
Value::Dict(values) => Self(
|
.into_iter()
|
||||||
values
|
.filter_map(|(k, v)| {
|
||||||
.into_iter()
|
let tag = Tag::from_bytes_lossy(k.as_bytes());
|
||||||
.filter_map(|(k, v)| {
|
let num = v.cast::<i64>().ok()?.try_into().ok()?;
|
||||||
let tag = Tag::from_bytes_lossy(k.as_bytes());
|
Some((tag, num))
|
||||||
let num = v.cast::<i64>().ok()?.try_into().ok()?;
|
})
|
||||||
Some((tag, num))
|
.collect(),
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = args.named("family")?.or_else(|| {
|
let list = args.named("family")?.or_else(|| {
|
||||||
let families: Vec<_> = args.all().collect();
|
let families: Vec<_> = args.all().collect();
|
||||||
(!families.is_empty()).then(|| FontDef(Rc::new(families)))
|
(!families.is_empty()).then(|| families)
|
||||||
});
|
});
|
||||||
|
|
||||||
let serif = args.named("serif")?;
|
let serif = args.named("serif")?;
|
||||||
let sans_serif = args.named("sans-serif")?;
|
let sans_serif = args.named("sans-serif")?;
|
||||||
let monospace = args.named("monospace")?;
|
let monospace = args.named("monospace")?;
|
||||||
let fallback = args.named("fallback")?;
|
let fallback = args.named("fallback")?;
|
||||||
|
|
||||||
let size = args.named::<Linear>("size")?.or_else(|| args.find());
|
|
||||||
let style = args.named("style")?;
|
let style = args.named("style")?;
|
||||||
let weight = args.named("weight")?;
|
let weight = args.named("weight")?;
|
||||||
let stretch = args.named("stretch")?;
|
let stretch = args.named("stretch")?;
|
||||||
let fill = args.named("fill")?.or_else(|| args.find());
|
let size = args.named::<Linear>("size")?.or_else(|| args.find());
|
||||||
let top_edge = args.named("top-edge")?;
|
let top_edge = args.named("top-edge")?;
|
||||||
let bottom_edge = args.named("bottom-edge")?;
|
let bottom_edge = args.named("bottom-edge")?;
|
||||||
|
let fill = args.named("fill")?.or_else(|| args.find());
|
||||||
let kerning = args.named("kerning")?;
|
let kerning = args.named("kerning")?;
|
||||||
let smallcaps = args.named("smallcaps")?;
|
let smallcaps = args.named("smallcaps")?;
|
||||||
let alternates = args.named("alternates")?;
|
let alternates = args.named("alternates")?;
|
||||||
@ -199,22 +195,6 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
let f = move |style_: &mut Style| {
|
let f = move |style_: &mut Style| {
|
||||||
let text = style_.text_mut();
|
let text = style_.text_mut();
|
||||||
|
|
||||||
if let Some(FontDef(list)) = &list {
|
|
||||||
text.families_mut().list = list.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(FamilyDef(serif)) = &serif {
|
|
||||||
text.families_mut().serif = serif.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(FamilyDef(sans_serif)) = &sans_serif {
|
|
||||||
text.families_mut().sans_serif = sans_serif.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(FamilyDef(monospace)) = &monospace {
|
|
||||||
text.families_mut().monospace = monospace.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(size) = size {
|
if let Some(size) = size {
|
||||||
text.size = size.resolve(text.size);
|
text.size = size.resolve(text.size);
|
||||||
}
|
}
|
||||||
@ -223,16 +203,21 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
text.fill = Paint::Color(fill);
|
text.fill = Paint::Color(fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
set!(text.fallback => fallback);
|
|
||||||
set!(text.variant.style => style);
|
set!(text.variant.style => style);
|
||||||
set!(text.variant.weight => weight);
|
set!(text.variant.weight => weight);
|
||||||
set!(text.variant.stretch => stretch);
|
set!(text.variant.stretch => stretch);
|
||||||
set!(text.top_edge => top_edge);
|
set!(text.top_edge => top_edge);
|
||||||
set!(text.bottom_edge => bottom_edge);
|
set!(text.bottom_edge => bottom_edge);
|
||||||
|
set!(text.fallback => fallback);
|
||||||
|
|
||||||
|
set!(text.families_mut().list => list.clone());
|
||||||
|
set!(text.families_mut().serif => serif.clone());
|
||||||
|
set!(text.families_mut().sans_serif => sans_serif.clone());
|
||||||
|
set!(text.families_mut().monospace => monospace.clone());
|
||||||
set!(text.features_mut().kerning => kerning);
|
set!(text.features_mut().kerning => kerning);
|
||||||
set!(text.features_mut().smallcaps => smallcaps);
|
set!(text.features_mut().smallcaps => smallcaps);
|
||||||
set!(text.features_mut().alternates => alternates);
|
set!(text.features_mut().alternates => alternates);
|
||||||
|
set!(text.features_mut().stylistic_set => stylistic_set);
|
||||||
set!(text.features_mut().ligatures.standard => ligatures);
|
set!(text.features_mut().ligatures.standard => ligatures);
|
||||||
set!(text.features_mut().ligatures.discretionary => discretionary_ligatures);
|
set!(text.features_mut().ligatures.discretionary => discretionary_ligatures);
|
||||||
set!(text.features_mut().ligatures.historical => historical_ligatures);
|
set!(text.features_mut().ligatures.historical => historical_ligatures);
|
||||||
@ -241,14 +226,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
set!(text.features_mut().numbers.position => number_position);
|
set!(text.features_mut().numbers.position => number_position);
|
||||||
set!(text.features_mut().numbers.slashed_zero => slashed_zero);
|
set!(text.features_mut().numbers.slashed_zero => slashed_zero);
|
||||||
set!(text.features_mut().numbers.fractions => fractions);
|
set!(text.features_mut().numbers.fractions => fractions);
|
||||||
|
set!(text.features_mut().raw => features.clone());
|
||||||
if let Some(StylisticSet(stylistic_set)) = stylistic_set {
|
|
||||||
text.features_mut().stylistic_set = stylistic_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(FeatureList(features)) = &features {
|
|
||||||
text.features_mut().raw = features.clone();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(if let Some(body) = body {
|
Ok(if let Some(body) = body {
|
||||||
@ -638,12 +616,10 @@ fn tags(features: &FontFeatures) -> Vec<Feature> {
|
|||||||
feat(b"salt", 1);
|
feat(b"salt", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let set_tag;
|
let storage;
|
||||||
if let Some(set) = features.stylistic_set {
|
if let StylisticSet(Some(set @ 1 ..= 20)) = features.stylistic_set {
|
||||||
if matches!(set, 1 ..= 20) {
|
storage = [b's', b's', b'0' + set / 10, b'0' + set % 10];
|
||||||
set_tag = [b's', b's', b'0' + set / 10, b'0' + set % 10];
|
feat(&storage, 1);
|
||||||
feat(&set_tag, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !features.ligatures.standard {
|
if !features.ligatures.standard {
|
||||||
|
@ -77,8 +77,8 @@ pub fn float(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("value")?;
|
let Spanned { v, span } = args.expect("value")?;
|
||||||
Ok(Value::Str(match v {
|
Ok(Value::Str(match v {
|
||||||
Value::Int(v) => format_str!("{}", v),
|
Value::Int(v) => format_eco!("{}", v),
|
||||||
Value::Float(v) => format_str!("{}", v),
|
Value::Float(v) => format_eco!("{}", v),
|
||||||
Value::Str(v) => v,
|
Value::Str(v) => v,
|
||||||
v => bail!(span, "cannot convert {} to string", v.type_name()),
|
v => bail!(span, "cannot convert {} to string", v.type_name()),
|
||||||
}))
|
}))
|
||||||
@ -87,7 +87,7 @@ pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
/// `rgb`: Create an RGB(A) color.
|
/// `rgb`: Create an RGB(A) color.
|
||||||
pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
Ok(Value::Color(Color::Rgba(
|
Ok(Value::Color(Color::Rgba(
|
||||||
if let Some(string) = args.find::<Spanned<Str>>() {
|
if let Some(string) = args.find::<Spanned<EcoString>>() {
|
||||||
match RgbaColor::from_str(&string.v) {
|
match RgbaColor::from_str(&string.v) {
|
||||||
Ok(color) => color,
|
Ok(color) => color,
|
||||||
Err(_) => bail!(string.span, "invalid hex string"),
|
Err(_) => bail!(string.span, "invalid hex string"),
|
||||||
@ -98,7 +98,7 @@ pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
let b = args.expect("blue component")?;
|
let b = args.expect("blue component")?;
|
||||||
let a = args.eat()?.unwrap_or(Spanned::new(1.0, Span::detached()));
|
let a = args.eat()?.unwrap_or(Spanned::new(1.0, Span::detached()));
|
||||||
let f = |Spanned { v, span }: Spanned<f64>| {
|
let f = |Spanned { v, span }: Spanned<f64>| {
|
||||||
if 0.0 <= v && v <= 1.0 {
|
if (0.0 ..= 1.0).contains(&v) {
|
||||||
Ok((v * 255.0).round() as u8)
|
Ok((v * 255.0).round() as u8)
|
||||||
} else {
|
} else {
|
||||||
bail!(span, "value must be between 0.0 and 1.0");
|
bail!(span, "value must be between 0.0 and 1.0");
|
||||||
@ -182,19 +182,19 @@ pub fn range(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
/// `lower`: Convert a string to lowercase.
|
/// `lower`: Convert a string to lowercase.
|
||||||
pub fn lower(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn lower(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
Ok(args.expect::<Str>("string")?.to_lowercase().into())
|
Ok(args.expect::<EcoString>("string")?.to_lowercase().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `upper`: Convert a string to uppercase.
|
/// `upper`: Convert a string to uppercase.
|
||||||
pub fn upper(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn upper(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
Ok(args.expect::<Str>("string")?.to_uppercase().into())
|
Ok(args.expect::<EcoString>("string")?.to_uppercase().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `len`: The length of a string, an array or a dictionary.
|
/// `len`: The length of a string, an array or a dictionary.
|
||||||
pub fn len(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn len(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("collection")?;
|
let Spanned { v, span } = args.expect("collection")?;
|
||||||
Ok(Value::Int(match v {
|
Ok(Value::Int(match v {
|
||||||
Value::Str(v) => v.len(),
|
Value::Str(v) => v.len() as i64,
|
||||||
Value::Array(v) => v.len(),
|
Value::Array(v) => v.len(),
|
||||||
Value::Dict(v) => v.len(),
|
Value::Dict(v) => v.len(),
|
||||||
v => bail!(
|
v => bail!(
|
||||||
|
@ -119,7 +119,7 @@ impl FsLoader {
|
|||||||
let path = path.strip_prefix(".").unwrap_or(path);
|
let path = path.strip_prefix(".").unwrap_or(path);
|
||||||
if let Ok(file) = File::open(path) {
|
if let Ok(file) = File::open(path) {
|
||||||
if let Ok(mmap) = unsafe { Mmap::map(&file) } {
|
if let Ok(mmap) = unsafe { Mmap::map(&file) } {
|
||||||
self.faces.extend(FaceInfo::parse(&path, &mmap));
|
self.faces.extend(FaceInfo::parse(path, &mmap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ impl<'s> Parser<'s> {
|
|||||||
self.eat();
|
self.eat();
|
||||||
rescan = false;
|
rescan = false;
|
||||||
} else if required {
|
} else if required {
|
||||||
self.push_error(format!("expected {}", end));
|
self.push_error(format_eco!("expected {}", end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +327,8 @@ impl Parser<'_> {
|
|||||||
pub fn unexpected(&mut self) {
|
pub fn unexpected(&mut self) {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Some(found) => {
|
Some(found) => {
|
||||||
let msg = format!("unexpected {}", found);
|
let msg = format_eco!("unexpected {}", found);
|
||||||
let error = NodeKind::Error(ErrorPos::Full, msg.into());
|
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||||
self.perform(error, Self::eat);
|
self.perform(error, Self::eat);
|
||||||
}
|
}
|
||||||
None => self.push_error("unexpected end of file"),
|
None => self.push_error("unexpected end of file"),
|
||||||
@ -339,8 +339,8 @@ impl Parser<'_> {
|
|||||||
pub fn expected(&mut self, thing: &str) {
|
pub fn expected(&mut self, thing: &str) {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Some(found) => {
|
Some(found) => {
|
||||||
let msg = format!("expected {}, found {}", thing, found);
|
let msg = format_eco!("expected {}, found {}", thing, found);
|
||||||
let error = NodeKind::Error(ErrorPos::Full, msg.into());
|
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||||
self.perform(error, Self::eat);
|
self.perform(error, Self::eat);
|
||||||
}
|
}
|
||||||
None => self.expected_at(thing),
|
None => self.expected_at(thing),
|
||||||
@ -400,8 +400,8 @@ impl Marker {
|
|||||||
|
|
||||||
/// Insert an error message that `what` was expected at the marker position.
|
/// Insert an error message that `what` was expected at the marker position.
|
||||||
pub fn expected(self, p: &mut Parser, what: &str) {
|
pub fn expected(self, p: &mut Parser, what: &str) {
|
||||||
let msg = format!("expected {}", what);
|
let msg = format_eco!("expected {}", what);
|
||||||
let error = NodeKind::Error(ErrorPos::Full, msg.into());
|
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||||
p.children.insert(self.0, GreenData::new(error, 0).into());
|
p.children.insert(self.0, GreenData::new(error, 0).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ impl<'s> Scanner<'s> {
|
|||||||
/// The full source string.
|
/// The full source string.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn src(&self) -> &'s str {
|
pub fn src(&self) -> &'s str {
|
||||||
&self.src
|
self.src
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slice out part of the source string.
|
/// Slice out part of the source string.
|
||||||
|
@ -239,7 +239,7 @@ impl<'s> Tokens<'s> {
|
|||||||
self.s.eat_assert('{');
|
self.s.eat_assert('{');
|
||||||
let sequence = self.s.eat_while(|c| c.is_ascii_alphanumeric());
|
let sequence = self.s.eat_while(|c| c.is_ascii_alphanumeric());
|
||||||
if self.s.eat_if('}') {
|
if self.s.eat_if('}') {
|
||||||
if let Some(c) = resolve_hex(&sequence) {
|
if let Some(c) = resolve_hex(sequence) {
|
||||||
NodeKind::UnicodeEscape(c)
|
NodeKind::UnicodeEscape(c)
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Error(
|
NodeKind::Error(
|
||||||
@ -270,7 +270,7 @@ impl<'s> Tokens<'s> {
|
|||||||
None => NodeKind::Ident(read.into()),
|
None => NodeKind::Ident(read.into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Text("#".into())
|
NodeKind::Text('#'.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ impl<'s> Tokens<'s> {
|
|||||||
} else if self.s.check_or(true, char::is_whitespace) {
|
} else if self.s.check_or(true, char::is_whitespace) {
|
||||||
NodeKind::Minus
|
NodeKind::Minus
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Text("-".into())
|
NodeKind::Text('-'.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ impl<'s> Tokens<'s> {
|
|||||||
NodeKind::Raw(Rc::new(resolve_raw(
|
NodeKind::Raw(Rc::new(resolve_raw(
|
||||||
column,
|
column,
|
||||||
backticks,
|
backticks,
|
||||||
self.s.get(start .. end).into(),
|
self.s.get(start .. end),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
let remaining = backticks - found;
|
let remaining = backticks - found;
|
||||||
@ -349,11 +349,10 @@ impl<'s> Tokens<'s> {
|
|||||||
NodeKind::Error(
|
NodeKind::Error(
|
||||||
ErrorPos::End,
|
ErrorPos::End,
|
||||||
if found == 0 {
|
if found == 0 {
|
||||||
format!("expected {} {}", remaining, noun)
|
format_eco!("expected {} {}", remaining, noun)
|
||||||
} else {
|
} else {
|
||||||
format!("expected {} more {}", remaining, noun)
|
format_eco!("expected {} more {}", remaining, noun)
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,11 +396,10 @@ impl<'s> Tokens<'s> {
|
|||||||
NodeKind::Error(
|
NodeKind::Error(
|
||||||
ErrorPos::End,
|
ErrorPos::End,
|
||||||
if !display || (!escaped && dollar) {
|
if !display || (!escaped && dollar) {
|
||||||
"expected closing dollar sign"
|
"expected closing dollar sign".into()
|
||||||
} else {
|
} else {
|
||||||
"expected closing bracket and dollar sign"
|
"expected closing bracket and dollar sign".into()
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +411,7 @@ impl<'s> Tokens<'s> {
|
|||||||
"auto" => NodeKind::Auto,
|
"auto" => NodeKind::Auto,
|
||||||
"true" => NodeKind::Bool(true),
|
"true" => NodeKind::Bool(true),
|
||||||
"false" => NodeKind::Bool(false),
|
"false" => NodeKind::Bool(false),
|
||||||
id => keyword(id).unwrap_or(NodeKind::Ident(id.into())),
|
id => keyword(id).unwrap_or_else(|| NodeKind::Ident(id.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,9 +459,7 @@ impl<'s> Tokens<'s> {
|
|||||||
"in" => NodeKind::Length(f, LengthUnit::In),
|
"in" => NodeKind::Length(f, LengthUnit::In),
|
||||||
"deg" => NodeKind::Angle(f, AngularUnit::Deg),
|
"deg" => NodeKind::Angle(f, AngularUnit::Deg),
|
||||||
"rad" => NodeKind::Angle(f, AngularUnit::Rad),
|
"rad" => NodeKind::Angle(f, AngularUnit::Rad),
|
||||||
_ => {
|
_ => NodeKind::Unknown(all.into()),
|
||||||
return NodeKind::Unknown(all.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Unknown(all.into())
|
NodeKind::Unknown(all.into())
|
||||||
|
@ -4,12 +4,14 @@ mod paper;
|
|||||||
|
|
||||||
pub use paper::*;
|
pub use paper::*;
|
||||||
|
|
||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use ttf_parser::Tag;
|
use ttf_parser::Tag;
|
||||||
|
|
||||||
use crate::font::*;
|
use crate::font::*;
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Defines a set of properties a template can be instantiated with.
|
/// Defines a set of properties a template can be instantiated with.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -192,7 +194,7 @@ impl TextStyle {
|
|||||||
&[]
|
&[]
|
||||||
};
|
};
|
||||||
|
|
||||||
head.iter().chain(core).chain(tail).map(String::as_str)
|
head.iter().chain(core).chain(tail).map(EcoString::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the `families` style mutably.
|
/// Access the `families` style mutably.
|
||||||
@ -232,33 +234,57 @@ impl Default for TextStyle {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct FamilyStyle {
|
pub struct FamilyStyle {
|
||||||
/// The user-defined list of font families.
|
/// The user-defined list of font families.
|
||||||
pub list: Rc<Vec<FontFamily>>,
|
pub list: Vec<FontFamily>,
|
||||||
/// Definition of serif font families.
|
/// Definition of serif font families.
|
||||||
pub serif: Rc<Vec<String>>,
|
pub serif: Vec<EcoString>,
|
||||||
/// Definition of sans-serif font families.
|
/// Definition of sans-serif font families.
|
||||||
pub sans_serif: Rc<Vec<String>>,
|
pub sans_serif: Vec<EcoString>,
|
||||||
/// Definition of monospace font families used for raw text.
|
/// Definition of monospace font families used for raw text.
|
||||||
pub monospace: Rc<Vec<String>>,
|
pub monospace: Vec<EcoString>,
|
||||||
/// Base fonts that are tried as last resort.
|
/// Base fonts that are tried as last resort.
|
||||||
pub base: Rc<Vec<String>>,
|
pub base: Vec<EcoString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FamilyStyle {
|
impl Default for FamilyStyle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
list: Rc::new(vec![FontFamily::SansSerif]),
|
list: vec![FontFamily::SansSerif],
|
||||||
serif: Rc::new(vec!["ibm plex serif".into()]),
|
serif: vec!["ibm plex serif".into()],
|
||||||
sans_serif: Rc::new(vec!["ibm plex sans".into()]),
|
sans_serif: vec!["ibm plex sans".into()],
|
||||||
monospace: Rc::new(vec!["ibm plex mono".into()]),
|
monospace: vec!["ibm plex mono".into()],
|
||||||
base: Rc::new(vec![
|
base: vec![
|
||||||
"ibm plex sans".into(),
|
"ibm plex sans".into(),
|
||||||
"latin modern math".into(),
|
"latin modern math".into(),
|
||||||
"twitter color emoji".into(),
|
"twitter color emoji".into(),
|
||||||
]),
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic or named font family.
|
||||||
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum FontFamily {
|
||||||
|
/// A family that has "serifs", small strokes attached to letters.
|
||||||
|
Serif,
|
||||||
|
/// A family in which glyphs do not have "serifs", small attached strokes.
|
||||||
|
SansSerif,
|
||||||
|
/// A family in which (almost) all glyphs are of equal width.
|
||||||
|
Monospace,
|
||||||
|
/// A specific family with a name.
|
||||||
|
Named(EcoString),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FontFamily {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
f.pad(match self {
|
||||||
|
Self::Serif => "serif",
|
||||||
|
Self::SansSerif => "sans-serif",
|
||||||
|
Self::Monospace => "monospace",
|
||||||
|
Self::Named(s) => s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether various kinds of ligatures should appear.
|
/// Whether various kinds of ligatures should appear.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct FontFeatures {
|
pub struct FontFeatures {
|
||||||
@ -269,7 +295,7 @@ pub struct FontFeatures {
|
|||||||
/// Whether to apply stylistic alternates. ("salt")
|
/// Whether to apply stylistic alternates. ("salt")
|
||||||
pub alternates: bool,
|
pub alternates: bool,
|
||||||
/// Which stylistic set to apply. ("ss01" - "ss20")
|
/// Which stylistic set to apply. ("ss01" - "ss20")
|
||||||
pub stylistic_set: Option<u8>,
|
pub stylistic_set: StylisticSet,
|
||||||
/// Configuration of ligature features.
|
/// Configuration of ligature features.
|
||||||
pub ligatures: LigatureFeatures,
|
pub ligatures: LigatureFeatures,
|
||||||
/// Configuration of numbers features.
|
/// Configuration of numbers features.
|
||||||
@ -284,7 +310,7 @@ impl Default for FontFeatures {
|
|||||||
kerning: true,
|
kerning: true,
|
||||||
smallcaps: false,
|
smallcaps: false,
|
||||||
alternates: false,
|
alternates: false,
|
||||||
stylistic_set: None,
|
stylistic_set: StylisticSet::default(),
|
||||||
ligatures: LigatureFeatures::default(),
|
ligatures: LigatureFeatures::default(),
|
||||||
numbers: NumberFeatures::default(),
|
numbers: NumberFeatures::default(),
|
||||||
raw: vec![],
|
raw: vec![],
|
||||||
@ -292,6 +318,16 @@ impl Default for FontFeatures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A stylistic set in a font face.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct StylisticSet(pub Option<u8>);
|
||||||
|
|
||||||
|
impl Default for StylisticSet {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether various kinds of ligatures should appear.
|
/// Whether various kinds of ligatures should appear.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct LigatureFeatures {
|
pub struct LigatureFeatures {
|
||||||
|
@ -65,9 +65,9 @@ impl Markup {
|
|||||||
NodeKind::Emph => Some(MarkupNode::Emph),
|
NodeKind::Emph => Some(MarkupNode::Emph),
|
||||||
NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())),
|
NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())),
|
||||||
NodeKind::UnicodeEscape(c) => Some(MarkupNode::Text((*c).into())),
|
NodeKind::UnicodeEscape(c) => Some(MarkupNode::Text((*c).into())),
|
||||||
NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())),
|
NodeKind::EnDash => Some(MarkupNode::Text('\u{2013}'.into())),
|
||||||
NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())),
|
NodeKind::EmDash => Some(MarkupNode::Text('\u{2014}'.into())),
|
||||||
NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())),
|
NodeKind::NonBreakingSpace => Some(MarkupNode::Text('\u{00A0}'.into())),
|
||||||
NodeKind::Math(math) => Some(MarkupNode::Math(math.as_ref().clone())),
|
NodeKind::Math(math) => Some(MarkupNode::Math(math.as_ref().clone())),
|
||||||
NodeKind::Raw(raw) => Some(MarkupNode::Raw(raw.as_ref().clone())),
|
NodeKind::Raw(raw) => Some(MarkupNode::Raw(raw.as_ref().clone())),
|
||||||
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
|
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
|
||||||
@ -175,7 +175,7 @@ impl EnumNode {
|
|||||||
self.0
|
self.0
|
||||||
.children()
|
.children()
|
||||||
.find_map(|node| match node.kind() {
|
.find_map(|node| match node.kind() {
|
||||||
NodeKind::EnumNumbering(num) => Some(num.clone()),
|
NodeKind::EnumNumbering(num) => Some(*num),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.expect("enum node is missing number")
|
.expect("enum node is missing number")
|
||||||
|
@ -30,7 +30,7 @@ impl Green {
|
|||||||
fn data(&self) -> &GreenData {
|
fn data(&self) -> &GreenData {
|
||||||
match self {
|
match self {
|
||||||
Green::Node(n) => &n.data,
|
Green::Node(n) => &n.data,
|
||||||
Green::Token(t) => &t,
|
Green::Token(t) => t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ impl Green {
|
|||||||
/// The node's children.
|
/// The node's children.
|
||||||
pub fn children(&self) -> &[Green] {
|
pub fn children(&self) -> &[Green] {
|
||||||
match self {
|
match self {
|
||||||
Green::Node(n) => &n.children(),
|
Green::Node(n) => n.children(),
|
||||||
Green::Token(_) => &[],
|
Green::Token(_) => &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,11 @@ impl Span {
|
|||||||
Self { end, ..self }
|
Self { end, ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the span is a single point.
|
||||||
|
pub fn is_empty(self) -> bool {
|
||||||
|
self.start == self.end
|
||||||
|
}
|
||||||
|
|
||||||
/// The byte length of the spanned region.
|
/// The byte length of the spanned region.
|
||||||
pub fn len(self) -> usize {
|
pub fn len(self) -> usize {
|
||||||
self.end - self.start
|
self.end - self.start
|
||||||
|
@ -2,11 +2,21 @@ use std::borrow::Borrow;
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Deref;
|
use std::ops::{Add, AddAssign, Deref};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::RcExt;
|
use super::RcExt;
|
||||||
|
|
||||||
|
/// Create a new [`EcoString`] from a format string.
|
||||||
|
macro_rules! format_eco {
|
||||||
|
($($tts:tt)*) => {{
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut s = $crate::util::EcoString::new();
|
||||||
|
write!(s, $($tts)*).unwrap();
|
||||||
|
s
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
/// An economical string with inline storage and clone-on-write semantics.
|
/// An economical string with inline storage and clone-on-write semantics.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EcoString(Repr);
|
pub struct EcoString(Repr);
|
||||||
@ -142,14 +152,38 @@ impl EcoString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the string to lowercase.
|
||||||
|
pub fn to_lowercase(&self) -> Self {
|
||||||
|
if let Repr::Small { mut buf, len } = self.0 {
|
||||||
|
if self.is_ascii() {
|
||||||
|
buf[.. usize::from(len)].make_ascii_lowercase();
|
||||||
|
return Self(Repr::Small { buf, len });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.as_str().to_lowercase().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the string to uppercase.
|
||||||
|
pub fn to_uppercase(&self) -> Self {
|
||||||
|
if let Repr::Small { mut buf, len } = self.0 {
|
||||||
|
if self.is_ascii() {
|
||||||
|
buf[.. usize::from(len)].make_ascii_uppercase();
|
||||||
|
return Self(Repr::Small { buf, len });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.as_str().to_uppercase().into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Repeat this string `n` times.
|
/// Repeat this string `n` times.
|
||||||
pub fn repeat(&self, n: usize) -> Self {
|
pub fn repeat(&self, n: usize) -> Self {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return Self::new();
|
return Self::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Repr::Small { buf, len } = &self.0 {
|
if let Repr::Small { buf, len } = self.0 {
|
||||||
let prev = usize::from(*len);
|
let prev = usize::from(len);
|
||||||
let new = prev.saturating_mul(n);
|
let new = prev.saturating_mul(n);
|
||||||
if new <= LIMIT {
|
if new <= LIMIT {
|
||||||
let src = &buf[.. prev];
|
let src = &buf[.. prev];
|
||||||
@ -193,7 +227,18 @@ impl Default for EcoString {
|
|||||||
|
|
||||||
impl Debug for EcoString {
|
impl Debug for EcoString {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
Debug::fmt(self.as_str(), f)
|
f.write_char('"')?;
|
||||||
|
for c in self.chars() {
|
||||||
|
match c {
|
||||||
|
'\\' => f.write_str(r"\\")?,
|
||||||
|
'"' => f.write_str(r#"\""#)?,
|
||||||
|
'\n' => f.write_str(r"\n")?,
|
||||||
|
'\r' => f.write_str(r"\r")?,
|
||||||
|
'\t' => f.write_str(r"\t")?,
|
||||||
|
_ => f.write_char(c)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_char('"')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +298,21 @@ impl Write for EcoString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Add for EcoString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: Self) -> Self::Output {
|
||||||
|
self += rhs;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for EcoString {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
self.push_str(rhs.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<str> for EcoString {
|
impl AsRef<str> for EcoString {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
self
|
self
|
||||||
@ -382,6 +442,21 @@ mod tests {
|
|||||||
assert_eq!(&v[.. 2], "ab");
|
assert_eq!(&v[.. 2], "ab");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_str_case() {
|
||||||
|
assert_eq!(EcoString::new().to_uppercase(), "");
|
||||||
|
assert_eq!(EcoString::from("abc").to_uppercase(), "ABC");
|
||||||
|
assert_eq!(EcoString::from("AΣ").to_lowercase(), "aς");
|
||||||
|
assert_eq!(
|
||||||
|
EcoString::from("a").repeat(100).to_uppercase(),
|
||||||
|
EcoString::from("A").repeat(100)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
EcoString::from("Ö").repeat(20).to_lowercase(),
|
||||||
|
EcoString::from("ö").repeat(20)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_str_repeat() {
|
fn test_str_repeat() {
|
||||||
// Test with empty string.
|
// Test with empty string.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Utilities.
|
//! Utilities.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
mod eco_string;
|
mod eco_string;
|
||||||
mod mac_roman;
|
mod mac_roman;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user