Re-enable *, _ and `.

This commit is contained in:
Laurenz 2020-01-05 11:18:13 +01:00
parent 7b84f3b553
commit bd384a2a63
9 changed files with 253 additions and 224 deletions

View File

@ -101,7 +101,7 @@ pub enum Command<'a> {
Add(Layout), Add(Layout),
AddMultiple(MultiLayout), AddMultiple(MultiLayout),
AddSpacing(Size, SpacingKind, GenericAxis), SpacingFunc(Size, SpacingKind, GenericAxis),
FinishLine, FinishLine,
FinishSpace, FinishSpace,

View File

@ -44,9 +44,19 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Node::Space => self.layout_space(), Node::Space => self.layout_space(),
Node::Newline => self.layout_paragraph()?, Node::Newline => self.layout_paragraph()?,
Node::ToggleItalics => {}, Node::ToggleItalics => self.style.text.variant.style.toggle(),
Node::ToggleBold => {}, Node::ToggleBolder => {
Node::ToggleMonospace => {}, self.style.text.variant.weight.0 += 300 *
if self.style.text.bolder { -1 } else { 1 };
self.style.text.bolder = !self.style.text.bolder;
}
Node::ToggleMonospace => {
let list = &mut self.style.text.fallback.list;
match list.get(0).map(|s| s.as_str()) {
Some("monospace") => { list.remove(0); },
_ => list.insert(0, "monospace".to_string()),
}
}
Node::Func(func) => self.layout_func(func)?, Node::Func(func) => self.layout_func(func)?,
} }
@ -98,7 +108,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Add(layout) => self.layouter.add(layout)?, Add(layout) => self.layouter.add(layout)?,
AddMultiple(layouts) => self.layouter.add_multiple(layouts)?, AddMultiple(layouts) => self.layouter.add_multiple(layouts)?,
AddSpacing(space, kind, axis) => match axis { SpacingFunc(space, kind, axis) => match axis {
Primary => self.layouter.add_primary_spacing(space, kind), Primary => self.layouter.add_primary_spacing(space, kind),
Secondary => self.layouter.add_secondary_spacing(space, kind)?, Secondary => self.layouter.add_secondary_spacing(space, kind)?,
} }

View File

@ -1,16 +1,17 @@
use crate::func::prelude::*; use crate::func::prelude::*;
use super::maps::{PosAxisMap, AlignmentKey}; use super::maps::{PosAxisMap, AlignmentKey};
function! { function! {
/// `align`: Aligns content along the layouting axes. /// `align`: Aligns content along the layouting axes.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Align { pub struct AlignFunc {
body: Option<SyntaxTree>, body: Option<SyntaxTree>,
map: PosAxisMap<AlignmentKey>, map: PosAxisMap<AlignmentKey>,
} }
parse(args, body, ctx) { parse(args, body, ctx) {
Align { AlignFunc {
body: parse!(optional: body, ctx), body: parse!(optional: body, ctx),
map: PosAxisMap::new(&mut args)?, map: PosAxisMap::new(&mut args)?,
} }
@ -28,7 +29,7 @@ function! {
match &self.body { match &self.body {
Some(body) => vec![AddMultiple(layout(&body, ctx)?)], Some(body) => vec![AddMultiple(layout(&body, ctx)?)],
None => vec![Command::SetAlignment(ctx.alignment)], None => vec![SetAlignment(ctx.alignment)],
} }
} }
} }

View File

@ -3,17 +3,18 @@ use smallvec::smallvec;
use crate::func::prelude::*; use crate::func::prelude::*;
use super::maps::ExtentMap; use super::maps::ExtentMap;
function! { function! {
/// `box`: Layouts content into a box. /// `box`: Layouts content into a box.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Boxed { pub struct BoxFunc {
body: SyntaxTree, body: SyntaxTree,
map: ExtentMap<PSize>, map: ExtentMap<PSize>,
debug: Option<bool>, debug: Option<bool>,
} }
parse(args, body, ctx) { parse(args, body, ctx) {
Boxed { BoxFunc {
body: parse!(optional: body, ctx).unwrap_or(SyntaxTree::new()), body: parse!(optional: body, ctx).unwrap_or(SyntaxTree::new()),
map: ExtentMap::new(&mut args, false)?, map: ExtentMap::new(&mut args, false)?,
debug: args.get_key_opt::<bool>("debug")?, debug: args.get_key_opt::<bool>("debug")?,

View File

@ -1,16 +1,17 @@
use crate::func::prelude::*; use crate::func::prelude::*;
use super::maps::PosAxisMap; use super::maps::PosAxisMap;
function! { function! {
/// `direction`: Sets the directions of the layouting axes. /// `direction`: Sets the directions of the layouting axes.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct DirectionChange { pub struct DirectionFunc {
body: Option<SyntaxTree>, body: Option<SyntaxTree>,
map: PosAxisMap<Direction>, map: PosAxisMap<Direction>,
} }
parse(args, body, ctx) { parse(args, body, ctx) {
DirectionChange { DirectionFunc {
body: parse!(optional: body, ctx), body: parse!(optional: body, ctx),
map: PosAxisMap::new(&mut args)?, map: PosAxisMap::new(&mut args)?,
} }

View File

@ -12,73 +12,168 @@ pub_use_mod!(align);
pub_use_mod!(boxed); pub_use_mod!(boxed);
pub_use_mod!(direction); pub_use_mod!(direction);
/// Create a scope with all standard functions. /// Create a scope with all standard functions.
pub fn std() -> Scope { pub fn std() -> Scope {
let mut std = Scope::new(); let mut std = Scope::new();
std.add::<Align>("align"); // Font setup
std.add::<Boxed>("box"); std.add::<FontFamilyFunc>("font.family");
std.add::<DirectionChange>("direction"); std.add::<FontStyleFunc>("font.style");
std.add::<FontWeightFunc>("font.weight");
std.add::<FontSizeFunc>("font.size");
std.add::<LineBreak>("n"); // Layout
std.add::<LineBreak>("line.break"); std.add::<AlignFunc>("align");
std.add::<ParBreak>("par.break"); std.add::<DirectionFunc>("direction");
std.add::<PageBreak>("page.break"); std.add_with_metadata::<ContentSpacingFunc>("par.spacing", ContentKind::Paragraph);
std.add_with_metadata::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
std.add_with_metadata::<ContentSpacingFunc>("line.spacing", ContentKind::Line);
std.add::<BoxFunc>("box");
std.add_with_metadata::<ContentSpacing>("word.spacing", ContentKind::Word); // Spacing
std.add_with_metadata::<ContentSpacing>("line.spacing", ContentKind::Line); std.add::<LineBreakFunc>("n");
std.add_with_metadata::<ContentSpacing>("par.spacing", ContentKind::Paragraph); std.add::<LineBreakFunc>("line.break");
std.add::<ParBreakFunc>("par.break");
std.add::<PageBreakFunc>("page.break");
std.add_with_metadata::<SpacingFunc>("spacing", None);
std.add_with_metadata::<SpacingFunc>("h", Some(Horizontal));
std.add_with_metadata::<SpacingFunc>("v", Some(Vertical));
std.add::<PageSize>("page.size"); // Page setup
std.add::<PageMargins>("page.margins"); std.add::<PageSizeFunc>("page.size");
std.add::<PageMarginsFunc>("page.margins");
std.add_with_metadata::<Spacing>("spacing", None);
std.add_with_metadata::<Spacing>("h", Some(Horizontal));
std.add_with_metadata::<Spacing>("v", Some(Vertical));
std.add_with_metadata::<FontFamily>("font.family", None);
std.add_with_metadata::<FontFamily>("mono", Some("monospace".to_string()));
std.add::<SetFontStyle>("font.style");
std.add::<SetFontWeight>("font.weight");
std.add::<FontSize>("font.size");
std std
} }
function! { // -------------------------------------------------------------------------- //
/// `line.break`, `n`: Ends the current line. // Font setup
#[derive(Debug, Default, PartialEq)]
pub struct LineBreak;
parse(default) function! {
layout() { vec![FinishLine] } /// `font.family`: Set the font family.
#[derive(Debug, PartialEq)]
pub struct FontFamilyFunc {
body: Option<SyntaxTree>,
family: String,
}
parse(args, body, ctx, meta) {
FontFamilyFunc {
body: parse!(optional: body, ctx),
family: args.get_pos::<String>()?,
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.fallback.list = vec![self.family.clone()];
styled(&self.body, &ctx, style)
}
} }
function! { function! {
/// `par.break`: Ends the current paragraph. /// `font.style`: Set the font style (normal / italic).
/// #[derive(Debug, PartialEq)]
/// self has the same effect as two subsequent newlines. pub struct FontStyleFunc {
#[derive(Debug, Default, PartialEq)] body: Option<SyntaxTree>,
pub struct ParBreak; style: FontStyle,
}
parse(default) parse(args, body, ctx) {
layout() { vec![BreakParagraph] } FontStyleFunc {
body: parse!(optional: body, ctx),
style: {
let s = args.get_pos::<String>()?;
match FontStyle::from_str(&s) {
Some(style) => style,
None => error!("invalid font style: `{}`", s),
}
}
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.variant.style = self.style;
styled(&self.body, &ctx, style)
}
} }
function! { function! {
/// `page.break`: Ends the current page. /// `font.weight`: Set text with a given weight.
#[derive(Debug, Default, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PageBreak; pub struct FontWeightFunc {
body: Option<SyntaxTree>,
parse(default) weight: FontWeight,
layout() { vec![BreakPage] }
} }
parse(args, body, ctx, meta) {
FontWeightFunc {
body: parse!(optional: body, ctx),
weight: match args.get_pos::<Expression>()? {
Expression::Num(weight) => {
let weight = weight.round() as i16;
FontWeight(
if weight < 100 { 100 }
else if weight <= 900 { weight }
else { 900 }
)
}
Expression::Ident(Ident(s)) => {
match FontWeight::from_str(&s) {
Some(weight) => weight,
None => error!("invalid font weight: `{}`", s),
}
}
_ => error!("expected identifier or number"),
},
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.variant.style.toggle();
styled(&self.body, &ctx, style)
}
}
function! {
/// `font.size`: Sets the font size.
#[derive(Debug, PartialEq)]
pub struct FontSizeFunc {
body: Option<SyntaxTree>,
size: ScaleSize,
}
parse(args, body, ctx) {
FontSizeFunc {
body: parse!(optional: body, ctx),
size: args.get_pos::<ScaleSize>()?,
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
match self.size {
ScaleSize::Absolute(size) => {
style.base_font_size = size;
style.font_scale = 1.0;
}
ScaleSize::Scaled(scale) => style.font_scale = scale,
}
styled(&self.body, &ctx, style)
}
}
// -------------------------------------------------------------------------- //
// Layout
function! { function! {
/// `word.spacing`, `line.spacing`, `par.spacing`: The spacing between /// `word.spacing`, `line.spacing`, `par.spacing`: The spacing between
/// words, lines or paragraphs as a multiple of the font size. /// words, lines or paragraphs as a multiple of the font size.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ContentSpacing { pub struct ContentSpacingFunc {
body: Option<SyntaxTree>, body: Option<SyntaxTree>,
content: ContentKind, content: ContentKind,
spacing: f32, spacing: f32,
@ -87,7 +182,7 @@ function! {
type Meta = ContentKind; type Meta = ContentKind;
parse(args, body, ctx, meta) { parse(args, body, ctx, meta) {
ContentSpacing { ContentSpacingFunc {
body: parse!(optional: body, ctx), body: parse!(optional: body, ctx),
content: meta, content: meta,
spacing: args.get_pos::<f64>()? as f32, spacing: args.get_pos::<f64>()? as f32,
@ -113,10 +208,81 @@ pub enum ContentKind {
Paragraph, Paragraph,
} }
// -------------------------------------------------------------------------- //
// Spacing
function! {
/// `line.break`, `n`: Ends the current line.
#[derive(Debug, Default, PartialEq)]
pub struct LineBreakFunc;
parse(default)
layout() { vec![FinishLine] }
}
function! {
/// `par.break`: Ends the current paragraph.
///
/// self has the same effect as two subsequent newlines.
#[derive(Debug, Default, PartialEq)]
pub struct ParBreakFunc;
parse(default)
layout() { vec![BreakParagraph] }
}
function! {
/// `page.break`: Ends the current page.
#[derive(Debug, Default, PartialEq)]
pub struct PageBreakFunc;
parse(default)
layout() { vec![BreakPage] }
}
function! {
/// `spacing`, `h`, `v`: Adds spacing along an axis.
#[derive(Debug, PartialEq)]
pub struct SpacingFunc {
axis: AxisKey,
spacing: FSize,
}
type Meta = Option<SpecificAxis>;
parse(args, body, _, meta) {
parse!(forbidden: body);
if let Some(axis) = meta {
SpacingFunc {
axis: AxisKey::Specific(axis),
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?,
}
} else if let Some(arg) = args.get_key_next() {
let axis = AxisKey::from_ident(&arg.v.key)
.map_err(|_| error!(@unexpected_argument))?;
let spacing = FSize::from_expr(arg.v.value)?;
SpacingFunc { axis, spacing }
} else {
error!("expected axis and spacing")
}
}
layout(self, ctx) {
let axis = self.axis.to_generic(ctx.axes);
let spacing = self.spacing.scaled(ctx.style.text.font_size());
vec![SpacingFunc(spacing, SpacingKind::Hard, axis)]
}
}
// -------------------------------------------------------------------------- //
// Page setup
function! { function! {
/// `page.size`: Set the size of pages. /// `page.size`: Set the size of pages.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum PageSize { pub enum PageSizeFunc {
Paper(Paper, bool), Paper(Paper, bool),
Custom(ExtentMap<PSize>), Custom(ExtentMap<PSize>),
} }
@ -125,11 +291,11 @@ function! {
parse!(forbidden: body); parse!(forbidden: body);
if let Some(name) = args.get_pos_opt::<Ident>()? { if let Some(name) = args.get_pos_opt::<Ident>()? {
let landscape = args.get_key_opt::<bool>("landscape")? let flip = args.get_key_opt::<bool>("flip")?
.unwrap_or(false); .unwrap_or(false);
PageSize::Paper(Paper::from_name(name.as_str())?, landscape) PageSizeFunc::Paper(Paper::from_name(name.as_str())?, flip)
} else { } else {
PageSize::Custom(ExtentMap::new(&mut args, true)?) PageSizeFunc::Custom(ExtentMap::new(&mut args, true)?)
} }
} }
@ -137,15 +303,15 @@ function! {
let mut style = ctx.style.page; let mut style = ctx.style.page;
match self { match self {
PageSize::Paper(paper, landscape) => { PageSizeFunc::Paper(paper, flip) => {
style.class = paper.class; style.class = paper.class;
style.dimensions = paper.dimensions; style.dimensions = paper.dimensions;
if *landscape { if *flip {
style.dimensions.swap(); style.dimensions.swap();
} }
} }
PageSize::Custom(map) => { PageSizeFunc::Custom(map) => {
style.class = PaperClass::Custom; style.class = PaperClass::Custom;
let map = map.dedup(ctx.axes)?; let map = map.dedup(ctx.axes)?;
@ -162,13 +328,13 @@ function! {
function! { function! {
/// `page.margins`: Sets the page margins. /// `page.margins`: Sets the page margins.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PageMargins { pub struct PageMarginsFunc {
map: PaddingMap, map: PaddingMap,
} }
parse(args, body) { parse(args, body) {
parse!(forbidden: body); parse!(forbidden: body);
PageMargins { PageMarginsFunc {
map: PaddingMap::new(&mut args)?, map: PaddingMap::new(&mut args)?,
} }
} }
@ -180,162 +346,8 @@ function! {
} }
} }
function! { // -------------------------------------------------------------------------- //
/// `spacing`, `h`, `v`: Adds spacing along an axis. // Helpers
#[derive(Debug, PartialEq)]
pub struct Spacing {
axis: AxisKey,
spacing: FSize,
}
type Meta = Option<SpecificAxis>;
parse(args, body, _, meta) {
parse!(forbidden: body);
if let Some(axis) = meta {
Spacing {
axis: AxisKey::Specific(axis),
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?,
}
} else if let Some(arg) = args.get_key_next() {
let axis = AxisKey::from_ident(&arg.v.key)
.map_err(|_| error!(@unexpected_argument))?;
let spacing = FSize::from_expr(arg.v.value)?;
Spacing { axis, spacing }
} else {
error!("expected axis and spacing")
}
}
layout(self, ctx) {
let axis = self.axis.to_generic(ctx.axes);
let spacing = self.spacing.scaled(ctx.style.text.font_size());
vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
}
}
function! {
/// `font.weight`, `bold`: Set text with a given weight.
#[derive(Debug, PartialEq)]
pub struct SetFontWeight {
body: Option<SyntaxTree>,
weight: FontWeight,
}
parse(args, body, ctx, meta) {
SetFontWeight {
body: parse!(optional: body, ctx),
weight: match args.get_pos::<Expression>()? {
Expression::Num(weight) => FontWeight(if weight < 0.0 {
0
} else if weight < 1000.0 {
weight.round() as u16
} else {
1000
}),
Expression::Ident(Ident(s)) => {
match FontWeight::from_str(&s) {
Some(weight) => weight,
None => error!("invalid font weight: `{}`", s),
}
}
_ => error!("expected identifier or number"),
},
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.variant.style.toggle();
styled(&self.body, &ctx, style)
}
}
function! {
/// `font.style`: Set the font style (normal / italic).
#[derive(Debug, PartialEq)]
pub struct SetFontStyle {
body: Option<SyntaxTree>,
style: FontStyle,
}
parse(args, body, ctx) {
SetFontStyle {
body: parse!(optional: body, ctx),
style: {
let s = args.get_pos::<String>()?;
match FontStyle::from_str(&s) {
Some(style) => style,
None => error!("invalid font style: `{}`", s),
}
}
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.variant.style = self.style;
styled(&self.body, &ctx, style)
}
}
function! {
/// `font.family`: Set the font family.
#[derive(Debug, PartialEq)]
pub struct FontFamily {
body: Option<SyntaxTree>,
family: String,
}
type Meta = Option<String>;
parse(args, body, ctx, meta) {
FontFamily {
body: parse!(optional: body, ctx),
family: if let Some(family) = meta {
family
} else {
args.get_pos::<String>()?
},
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
style.fallback.list = vec![self.family.clone()];
styled(&self.body, &ctx, style)
}
}
function! {
/// `font.size`: Sets the font size.
#[derive(Debug, PartialEq)]
pub struct FontSize {
body: Option<SyntaxTree>,
size: ScaleSize,
}
parse(args, body, ctx) {
FontSize {
body: parse!(optional: body, ctx),
size: args.get_pos::<ScaleSize>()?,
}
}
layout(self, ctx) {
let mut style = ctx.style.text.clone();
match self.size {
ScaleSize::Absolute(size) => {
style.base_font_size = size;
style.font_scale = 1.0;
}
ScaleSize::Scaled(scale) => style.font_scale = scale,
}
styled(&self.body, &ctx, style)
}
}
/// Layout the body with the style or update the style if there is no body. /// Layout the body with the style or update the style if there is no body.
fn styled<'a>( fn styled<'a>(

View File

@ -20,6 +20,9 @@ pub struct TextStyle {
pub fallback: FontFallbackTree, pub fallback: FontFallbackTree,
/// The selected font variant. /// The selected font variant.
pub variant: FontVariant, pub variant: FontVariant,
/// Whether the bolder toggle is active or inactive. This determines
/// whether the next `*` adds or removes font weight.
pub bolder: bool,
/// The base font size. /// The base font size.
pub base_font_size: Size, pub base_font_size: Size,
/// The font scale to apply on the base font size. /// The font scale to apply on the base font size.
@ -80,6 +83,7 @@ impl Default for TextStyle {
style: FontStyle::Normal, style: FontStyle::Normal,
weight: FontWeight(400), weight: FontWeight(400),
}, },
bolder: false,
base_font_size: Size::pt(11.0), base_font_size: Size::pt(11.0),
font_scale: 1.0, font_scale: 1.0,
word_spacing_scale: 0.25, word_spacing_scale: 0.25,

View File

@ -72,11 +72,11 @@ pub enum Node {
Space, Space,
/// A line feed. /// A line feed.
Newline, Newline,
/// Indicates that italics were enabled / disabled. /// Indicates that italics were toggled.
ToggleItalics, ToggleItalics,
/// Indicates that boldface was enabled / disabled. /// Indicates that bolder text was toggled.
ToggleBold, ToggleBolder,
/// Indicates that monospace was enabled / disabled. /// Indicates that monospace was toggled.
ToggleMonospace, ToggleMonospace,
/// Literal text. /// Literal text.
Text(String), Text(String),

View File

@ -73,7 +73,7 @@ impl<'s> Parser<'s> {
// Modifiers. // Modifiers.
Underscore => self.append_consumed(Node::ToggleItalics, token.span), Underscore => self.append_consumed(Node::ToggleItalics, token.span),
Star => self.append_consumed(Node::ToggleBold, token.span), Star => self.append_consumed(Node::ToggleBolder, token.span),
Backtick => self.append_consumed(Node::ToggleMonospace, token.span), Backtick => self.append_consumed(Node::ToggleMonospace, token.span),
// Normal text. // Normal text.