diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-10-10 23:36:17 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-10-10 23:38:03 +0200 |
| commit | 8f788f9a4f5e970bbe6147987b711470d57aca8d (patch) | |
| tree | cc89008dfdfc62ecf4eb2517d92ec7c7095fa573 /src/library | |
| parent | 61470fba68e9f19b481034427add5f3d8cfbc0a8 (diff) | |
Add standard `align` function and support right-alignment ➡️
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/align.rs | 51 | ||||
| -rw-r--r-- | src/library/mod.rs | 34 | ||||
| -rw-r--r-- | src/library/styles.rs | 64 |
3 files changed, 149 insertions, 0 deletions
diff --git a/src/library/align.rs b/src/library/align.rs new file mode 100644 index 00000000..4b2ee8c3 --- /dev/null +++ b/src/library/align.rs @@ -0,0 +1,51 @@ +//! Alignment function. + +use super::prelude::*; +use crate::layout::Alignment; + + +/// Allows to align content in different ways. +#[derive(Debug, PartialEq)] +pub struct AlignFunc { + alignment: Alignment, + body: Option<SyntaxTree>, +} + +impl Function for AlignFunc { + fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) + -> ParseResult<Self> where Self: Sized { + + if header.args.len() != 1 || !header.kwargs.is_empty() { + return err("expected exactly one positional argument specifying the alignment"); + } + + let alignment = if let Expression::Ident(ident) = &header.args[0] { + match ident.as_str() { + "left" => Alignment::Left, + "right" => Alignment::Right, + s => return err(format!("invalid alignment specifier: '{}'", s)), + } + } else { + return err(format!("expected alignment specifier, found: '{}'", header.args[0])); + }; + + let body = if let Some(body) = body { + Some(parse(body, ctx)?) + } else { + None + }; + + Ok(AlignFunc { alignment, body }) + } + + fn layout(&self, mut ctx: LayoutContext) -> LayoutResult<Option<Layout>> { + if let Some(body) = &self.body { + // Override the previous alignment and do the layouting. + ctx.space.alignment = self.alignment; + layout(body, ctx) + .map(|l| Some(Layout::Boxed(l))) + } else { + unimplemented!("context-modifying align func") + } + } +} diff --git a/src/library/mod.rs b/src/library/mod.rs new file mode 100644 index 00000000..9323c4c0 --- /dev/null +++ b/src/library/mod.rs @@ -0,0 +1,34 @@ +//! The standard library for the _Typst_ language. + +use crate::func::Scope; + +mod align; +mod styles; + +/// Useful imports for creating your own functions. +pub mod prelude { + pub use crate::syntax::{SyntaxTree, FuncHeader, Expression}; + pub use crate::parsing::{parse, ParseContext, ParseResult, ParseError}; + pub use crate::layout::{layout, Layout, LayoutContext, LayoutResult, LayoutError}; + pub use crate::layout::flex::FlexLayout; + pub use crate::layout::boxed::BoxLayout; + pub use crate::func::Function; + + pub fn err<S: Into<String>, T>(message: S) -> ParseResult<T> { + Err(ParseError::new(message)) + } +} + +pub use align::AlignFunc; +pub use styles::{ItalicFunc, BoldFunc, MonospaceFunc}; + + +/// Create a scope with all standard functions. +pub fn std() -> Scope { + let mut std = Scope::new(); + std.add::<BoldFunc>("bold"); + std.add::<ItalicFunc>("italic"); + std.add::<MonospaceFunc>("mono"); + std.add::<AlignFunc>("align"); + std +} diff --git a/src/library/styles.rs b/src/library/styles.rs new file mode 100644 index 00000000..c84b9fed --- /dev/null +++ b/src/library/styles.rs @@ -0,0 +1,64 @@ +//! Basic style functions: bold, italic, monospace. + +use super::prelude::*; +use toddle::query::FontClass; + + + +macro_rules! style_func { + ($(#[$outer:meta])* pub struct $struct:ident { $name:expr }, + $style:ident => $style_change:block) => { + $(#[$outer])* + #[derive(Debug, PartialEq)] + pub struct $struct { body: SyntaxTree } + + impl Function for $struct { + fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) + -> ParseResult<Self> where Self: Sized { + // Accept only invocations without arguments and with body. + if header.args.is_empty() && header.kwargs.is_empty() { + if let Some(body) = body { + Ok($struct { body: parse(body, ctx)? }) + } else { + Err(ParseError::new(format!("expected body for function `{}`", $name))) + } + } else { + Err(ParseError::new(format!("unexpected arguments to function `{}`", $name))) + } + } + + fn layout(&self, ctx: LayoutContext) -> LayoutResult<Option<Layout>> { + // Change the context. + let mut $style = ctx.style.clone(); + $style_change + + // Create a box and put it into a flex layout. + let boxed = layout(&self.body, LayoutContext { + style: &$style, + .. ctx + })?; + let flex = FlexLayout::from_box(boxed); + + Ok(Some(Layout::Flex(flex))) + } + } + }; +} + +style_func! { + /// Typesets text in bold. + pub struct BoldFunc { "bold" }, + style => { style.toggle_class(FontClass::Bold) } +} + +style_func! { + /// Typesets text in italics. + pub struct ItalicFunc { "italic" }, + style => { style.toggle_class(FontClass::Italic) } +} + +style_func! { + /// Typesets text in monospace. + pub struct MonospaceFunc { "mono" }, + style => { style.toggle_class(FontClass::Monospace) } +} |
