summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-10 23:36:17 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-10 23:38:03 +0200
commit8f788f9a4f5e970bbe6147987b711470d57aca8d (patch)
treecc89008dfdfc62ecf4eb2517d92ec7c7095fa573 /src/library
parent61470fba68e9f19b481034427add5f3d8cfbc0a8 (diff)
Add standard `align` function and support right-alignment ➡️
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs51
-rw-r--r--src/library/mod.rs34
-rw-r--r--src/library/styles.rs64
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) }
+}