summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-03 16:01:23 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-03 16:04:55 +0200
commitdbfb3d2ced91e56314dfabbb4df9a338926c0a7a (patch)
tree678264cb18f8abc81ebe28077f5aef2df4e5a4bd
parent5a8f2fb73ddafba9fdbe952385ae2676126183ae (diff)
Formatting, documentation and small improvements 🧽
-rw-r--r--Cargo.toml2
-rw-r--r--main/main.rs43
-rw-r--r--src/diagnostic.rs6
-rw-r--r--src/export/pdf.rs133
-rw-r--r--src/font.rs21
-rw-r--r--src/func.rs27
-rw-r--r--src/geom.rs66
-rw-r--r--src/layout/elements.rs46
-rw-r--r--src/layout/line.rs122
-rw-r--r--src/layout/mod.rs106
-rw-r--r--src/layout/primitive.rs91
-rw-r--r--src/layout/stack.rs122
-rw-r--r--src/layout/text.rs41
-rw-r--r--src/layout/tree.rs100
-rw-r--r--src/length.rs4
-rw-r--r--src/lib.rs93
-rw-r--r--src/library/font.rs57
-rw-r--r--src/library/layout.rs10
-rw-r--r--src/library/mod.rs50
-rw-r--r--src/library/page.rs4
-rw-r--r--src/library/spacing.rs10
-rw-r--r--src/macros.rs4
-rw-r--r--src/paper.rs13
-rw-r--r--src/style.rs19
-rw-r--r--src/syntax/decoration.rs32
-rw-r--r--src/syntax/expr.rs156
-rw-r--r--src/syntax/mod.rs20
-rw-r--r--src/syntax/parsing.rs116
-rw-r--r--src/syntax/scope.rs42
-rw-r--r--src/syntax/span.rs60
-rw-r--r--src/syntax/test.rs30
-rw-r--r--src/syntax/tokens.rs37
-rw-r--r--src/syntax/tree.rs18
-rw-r--r--src/syntax/value.rs30
-rw-r--r--tests/test_typeset.rs56
35 files changed, 835 insertions, 952 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c2f6b921..53f6a2e2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,7 @@ fs = ["fontdock/fs"]
[dev-dependencies]
futures-executor = "0.3"
serde_json = "1"
-raqote = { version = "0.7", default-features = false }
+raqote = { version = "0.8", default-features = false }
[[test]]
name = "test-typeset"
diff --git a/main/main.rs b/main/main.rs
index a04b163c..0d67241c 100644
--- a/main/main.rs
+++ b/main/main.rs
@@ -1,44 +1,37 @@
use std::cell::RefCell;
-use std::error::Error;
-use std::fs::{File, read_to_string};
+use std::fs::{read_to_string, File};
use std::io::BufWriter;
use std::path::{Path, PathBuf};
use std::rc::Rc;
-use futures_executor::block_on;
use fontdock::fs::{FsIndex, FsProvider};
use fontdock::FontLoader;
-use typstc::Typesetter;
-use typstc::font::DynProvider;
+use futures_executor::block_on;
+
use typstc::export::pdf;
+use typstc::font::DynProvider;
+use typstc::Typesetter;
fn main() {
- if let Err(err) = run() {
- eprintln!("error: {}", err);
- std::process::exit(1);
- }
-}
-
-fn run() -> Result<(), Box<dyn Error>> {
- let args: Vec<String> = std::env::args().collect();
+ let args: Vec<_> = std::env::args().collect();
if args.len() < 2 || args.len() > 3 {
println!("Usage: typst src.typ [out.pdf]");
- std::process::exit(0);
+ return;
}
- let source = Path::new(&args[1]);
- let dest = if args.len() <= 2 {
- source.with_extension("pdf")
+ let src_path = Path::new(&args[1]);
+ let dest_path = if args.len() <= 2 {
+ src_path.with_extension("pdf")
} else {
PathBuf::from(&args[2])
};
- if source == dest {
- Err("source and destination path are the same")?;
+ if src_path == dest_path {
+ panic!("source and destination path are the same");
}
- let src = read_to_string(source)
- .map_err(|_| "failed to read from source file")?;
+ let src = read_to_string(src_path)
+ .expect("failed to read from source file");
let mut index = FsIndex::new();
index.search_dir("fonts");
@@ -53,8 +46,10 @@ fn run() -> Result<(), Box<dyn Error>> {
let typesetter = Typesetter::new(loader.clone());
let layouts = block_on(typesetter.typeset(&src)).output;
- let writer = BufWriter::new(File::create(&dest)?);
- pdf::export(&layouts, &loader, writer)?;
+ let file = File::create(&dest_path)
+ .expect("failed to create output file");
- Ok(())
+ let writer = BufWriter::new(file);
+ pdf::export(&layouts, &loader, writer)
+ .expect("failed to export pdf");
}
diff --git a/src/diagnostic.rs b/src/diagnostic.rs
index fc332aac..2c649dac 100644
--- a/src/diagnostic.rs
+++ b/src/diagnostic.rs
@@ -1,8 +1,8 @@
-//! Diagnostics (errors / warnings) in source code.
+//! Diagnostics for source code.
//!
//! There are no fatal errors. The document will always compile and yield a
-//! layout. However, this is a best effort process and bad things will still
-//! generate errors and warnings.
+//! layout on a best effort process, generating diagnostics for incorrect
+//! things.
#[cfg(feature = "serialize")]
use serde::Serialize;
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index 84023c71..cd417a9c 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -1,28 +1,31 @@
-//! Exporting of layouts into _PDF_ documents.
+//! Exporting into _PDF_ documents.
use std::collections::HashMap;
use std::io::{self, Write};
-use tide::{PdfWriter, Rect, Ref, Trailer, Version};
+use fontdock::FaceId;
use tide::content::Content;
use tide::doc::{Catalog, Page, PageTree, Resource, Text};
use tide::font::{
- CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font,
- CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord,
+ CIDFont, CIDFontType, CIDSystemInfo, CMap, CMapEncoding, FontDescriptor,
+ FontFlags, FontStream, GlyphUnit, Type0Font, WidthRecord,
};
-
-use fontdock::FaceId;
+use tide::{PdfWriter, Rect, Ref, Trailer, Version};
use ttf_parser::{name_id, GlyphId};
-use crate::SharedFontLoader;
-use crate::layout::{MultiLayout, BoxLayout};
use crate::layout::elements::LayoutElement;
+use crate::layout::{BoxLayout, MultiLayout};
use crate::length::Length;
+use crate::SharedFontLoader;
-/// Export a layouted list of boxes. The same font loader as used for
-/// layouting needs to be passed in here since the layout only contains
-/// indices referencing the loaded faces. The raw PDF ist written into the
-/// target writable, returning the number of bytes written.
+/// Export a list of layouts into a _PDF_ document.
+///
+/// This creates one page per layout. Additionally to the layouts, you need to
+/// pass in the font loader used for typesetting such that the fonts can be
+/// included in the _PDF_.
+///
+/// The raw _PDF_ is written into the `target` writable, returning the number of
+/// bytes written.
pub fn export<W: Write>(
layout: &MultiLayout,
loader: &SharedFontLoader,
@@ -31,22 +34,20 @@ pub fn export<W: Write>(
PdfExporter::new(layout, loader, target)?.write()
}
-/// The data relevant to the export of one document.
struct PdfExporter<'a, W: Write> {
writer: PdfWriter<W>,
layouts: &'a MultiLayout,
loader: &'a SharedFontLoader,
- /// Since we cross-reference pages and faces with their IDs already in the
- /// document catalog, we need to know exactly which ID is used for what from
- /// the beginning. Thus, we compute a range for each category of object and
- /// stored these here.
+ /// We need to know exactly which indirect reference id will be used for
+ /// which objects up-front to correctly declare the document catalogue, page
+ /// tree and so on. These offsets are computed in the beginning and stored
+ /// here.
offsets: Offsets,
// Font remapping, see below at `remap_fonts`.
to_pdf: HashMap<FaceId, usize>,
- to_fontdock: Vec<FaceId>,
+ to_layout: Vec<FaceId>,
}
-/// Indicates which range of PDF IDs will be used for which contents.
struct Offsets {
catalog: Ref,
page_tree: Ref,
@@ -58,27 +59,24 @@ struct Offsets {
const NUM_OBJECTS_PER_FONT: u32 = 5;
impl<'a, W: Write> PdfExporter<'a, W> {
- /// Prepare the export. Only once [`ExportProcess::write`] is called the
- /// writing really happens.
fn new(
layouts: &'a MultiLayout,
loader: &'a SharedFontLoader,
target: W,
- ) -> io::Result<PdfExporter<'a, W>> {
+ ) -> io::Result<Self> {
let (to_pdf, to_fontdock) = remap_fonts(layouts);
let offsets = calculate_offsets(layouts.len(), to_pdf.len());
- Ok(PdfExporter {
+ Ok(Self {
writer: PdfWriter::new(target),
layouts,
offsets,
to_pdf,
- to_fontdock,
+ to_layout: to_fontdock,
loader,
})
}
- /// Write everything (writing entry point).
fn write(&mut self) -> io::Result<usize> {
self.writer.write_header(Version::new(1, 7))?;
self.write_preface()?;
@@ -89,14 +87,13 @@ impl<'a, W: Write> PdfExporter<'a, W> {
Ok(self.writer.written())
}
- /// Write the document catalog and page tree.
fn write_preface(&mut self) -> io::Result<()> {
// The document catalog.
self.writer.write_obj(self.offsets.catalog, &Catalog::new(self.offsets.page_tree))?;
// The font resources.
let start = self.offsets.fonts.0;
- let fonts = (0 .. self.to_pdf.len() as u32).map(|i| {
+ let fonts = (0..self.to_pdf.len() as u32).map(|i| {
Resource::Font(i + 1, start + (NUM_OBJECTS_PER_FONT * i))
});
@@ -109,11 +106,10 @@ impl<'a, W: Write> PdfExporter<'a, W> {
)?;
// The page objects (non-root nodes in the page tree).
- let iter = ids(self.offsets.pages)
+ for ((page_id, content_id), page) in ids(self.offsets.pages)
.zip(ids(self.offsets.contents))
- .zip(self.layouts);
-
- for ((page_id, content_id), page) in iter {
+ .zip(self.layouts)
+ {
let rect = Rect::new(
0.0,
0.0,
@@ -132,7 +128,6 @@ impl<'a, W: Write> PdfExporter<'a, W> {
Ok(())
}
- /// Write the contents of all pages.
fn write_pages(&mut self) -> io::Result<()> {
for (id, page) in ids(self.offsets.contents).zip(self.layouts) {
self.write_page(id, &page)?;
@@ -140,11 +135,11 @@ impl<'a, W: Write> PdfExporter<'a, W> {
Ok(())
}
- /// Write the content of a page.
fn write_page(&mut self, id: u32, page: &BoxLayout) -> io::Result<()> {
- // Moves and face switches are always cached and only flushed once
- // needed.
let mut text = Text::new();
+
+ // Font switching actions are only written when the face used for
+ // shaped text changes. Hence, we need to remember the active face.
let mut face = FaceId::MAX;
let mut size = 0.0;
@@ -163,7 +158,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
let x = Length::raw(pos.x).as_pt();
let y = Length::raw(page.size.y - pos.y - size).as_pt();
text.tm(1.0, 0.0, 0.0, 1.0, x as f32, y as f32);
- text.tj(shaped.encode_glyphs());
+ text.tj(shaped.encode_glyphs_be());
}
}
}
@@ -173,11 +168,10 @@ impl<'a, W: Write> PdfExporter<'a, W> {
Ok(())
}
- /// Write all the fonts.
fn write_fonts(&mut self) -> io::Result<()> {
let mut id = self.offsets.fonts.0;
- for &face_id in &self.to_fontdock {
+ for &face_id in &self.to_layout {
let loader = self.loader.borrow();
let face = loader.get_loaded(face_id);
@@ -223,6 +217,23 @@ impl<'a, W: Write> PdfExporter<'a, W> {
flags.insert(FontFlags::SYMBOLIC);
flags.insert(FontFlags::SMALL_CAP);
+ let num_glyphs = face.number_of_glyphs();
+ let widths: Vec<_> = (0..num_glyphs)
+ .map(|g| face.glyph_hor_advance(GlyphId(g)).unwrap_or(0))
+ .map(|w| to_glyph_unit(w as f64))
+ .collect();
+
+ let mut mapping = vec![];
+ for subtable in face.character_mapping_subtables() {
+ subtable.codepoints(|n| {
+ if let Some(c) = std::char::from_u32(n) {
+ if let Some(g) = face.glyph_index(c) {
+ mapping.push((g.0, c));
+ }
+ }
+ })
+ }
+
// Write the base font object referencing the CID font.
self.writer.write_obj(
id,
@@ -234,12 +245,6 @@ impl<'a, W: Write> PdfExporter<'a, W> {
.to_unicode(id + 3),
)?;
- let num_glyphs = face.number_of_glyphs();
- let widths: Vec<_> = (0 .. num_glyphs)
- .map(|g| face.glyph_hor_advance(GlyphId(g)).unwrap_or(0))
- .map(|w| to_glyph_unit(w as f64))
- .collect();
-
// Write the CID font referencing the font descriptor.
self.writer.write_obj(
id + 1,
@@ -252,8 +257,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
.widths(vec![WidthRecord::Start(0, widths)]),
)?;
- // Write the font descriptor (contains the global information about
- // the font).
+ // Write the font descriptor (contains metrics about the font).
self.writer.write_obj(id + 2,
FontDescriptor::new(base_font, flags, italic_angle)
.font_bbox(bbox)
@@ -264,25 +268,15 @@ impl<'a, W: Write> PdfExporter<'a, W> {
.font_file_2(id + 4)
)?;
- let mut mapping = vec![];
- for subtable in face.character_mapping_subtables() {
- subtable.codepoints(|n| {
- if let Some(c) = std::char::from_u32(n) {
- if let Some(g) = face.glyph_index(c) {
- mapping.push((g.0, c));
- }
- }
- })
- }
-
- // Write the CMap, which maps glyph ID's to unicode codepoints.
+ // Write the CMap, which maps glyph ids back to unicode codepoints
+ // to enable copying out of the PDF.
self.writer.write_obj(id + 3, &CMap::new(
"Custom",
system_info,
mapping,
))?;
- // Finally write the subsetted font bytes.
+ // Write the face's bytes.
self.writer.write_obj(id + 4, &FontStream::new(face.data()))?;
id += NUM_OBJECTS_PER_FONT;
@@ -294,28 +288,28 @@ impl<'a, W: Write> PdfExporter<'a, W> {
/// Assigns a new PDF-internal index to each used face and returns two mappings:
/// - Forwards from the old face ids to the new pdf indices (hash map)
-/// - Backwards from the pdf indices to the old ids (vec)
+/// - Backwards from the pdf indices to the old face ids (vec)
fn remap_fonts(layouts: &MultiLayout) -> (HashMap<FaceId, usize>, Vec<FaceId>) {
let mut to_pdf = HashMap::new();
- let mut to_fontdock = vec![];
+ let mut to_layout = vec![];
- // We want to find out which fonts are used at all. To do that, look at each
- // text element to find out which font is uses.
+ // We want to find out which font faces are used at all. To do that, look at
+ // each text element to find out which face is uses.
for layout in layouts {
for (_, element) in &layout.elements.0 {
let LayoutElement::Text(shaped) = element;
to_pdf.entry(shaped.face).or_insert_with(|| {
- let next_id = to_fontdock.len();
- to_fontdock.push(shaped.face);
+ let next_id = to_layout.len();
+ to_layout.push(shaped.face);
next_id
});
}
}
- (to_pdf, to_fontdock)
+ (to_pdf, to_layout)
}
-/// We need to know in advance which IDs to use for which objects to
+/// We need to know in advance which ids to use for which objects to
/// cross-reference them. Therefore, we calculate the indices in the beginning.
fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
let catalog = 1;
@@ -333,7 +327,6 @@ fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
}
}
-/// Create an iterator from a reference pair.
-fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item=Ref> {
- start ..= end
+fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
+ start..=end
}
diff --git a/src/font.rs b/src/font.rs
index 5d81b6fc..c37c913e 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -3,14 +3,15 @@
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
+
+use fontdock::{ContainsChar, FaceFromVec, FontLoader, FontProvider};
use ttf_parser::Face;
-use fontdock::{FontLoader, FontProvider, ContainsChar, FaceFromVec};
/// A referenced-count shared font loader backed by a dynamic provider.
pub type SharedFontLoader = Rc<RefCell<FontLoader<Box<DynProvider>>>>;
/// The dynamic font provider type backing the font loader.
-pub type DynProvider = dyn FontProvider<Face=OwnedFace>;
+pub type DynProvider = dyn FontProvider<Face = OwnedFace>;
/// An owned font face.
pub struct OwnedFace {
@@ -18,6 +19,13 @@ pub struct OwnedFace {
face: Face<'static>,
}
+impl OwnedFace {
+ /// The raw face data.
+ pub fn data(&self) -> &[u8] {
+ &self.data
+ }
+}
+
impl FaceFromVec for OwnedFace {
fn from_vec(vec: Vec<u8>, i: u32) -> Option<Self> {
// The vec's location is stable in memory since we don't touch it and
@@ -26,20 +34,13 @@ impl FaceFromVec for OwnedFace {
std::slice::from_raw_parts(vec.as_ptr(), vec.len())
};
- Some(OwnedFace {
+ Some(Self {
data: vec,
face: Face::from_slice(slice, i).ok()?,
})
}
}
-impl OwnedFace {
- /// The raw face data.
- pub fn data(&self) -> &[u8] {
- &self.data
- }
-}
-
impl ContainsChar for OwnedFace {
fn contains_char(&self, c: char) -> bool {
self.glyph_index(c).is_some()
diff --git a/src/func.rs b/src/func.rs
index bb2862d2..09870895 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -1,20 +1,20 @@
//! Tools for building custom functions.
-use crate::Feedback;
-use crate::syntax::span::{Span, Spanned};
-use crate::syntax::parsing::{parse, ParseState};
-use crate::syntax::tree::SyntaxTree;
-
/// Useful things for creating functions.
pub mod prelude {
pub use crate::layout::prelude::*;
pub use crate::layout::Command::{self, *};
- pub use crate::syntax::prelude::*;
pub use crate::style::*;
- pub use super::{OptionExt, parse_maybe_body, expect_no_body};
+ pub use crate::syntax::prelude::*;
+ pub use super::{expect_no_body, parse_maybe_body, OptionExt};
}
-/// Extra methods on [`Options`](Option) used for function argument parsing.
+use crate::syntax::parsing::{parse, ParseState};
+use crate::syntax::span::{Span, Spanned};
+use crate::syntax::tree::SyntaxTree;
+use crate::Feedback;
+
+/// Extra methods on `Option`s used for function argument parsing.
pub trait OptionExt<T>: Sized {
/// Calls `f` with `val` if this is `Some(val)`.
fn with(self, f: impl FnOnce(T));
@@ -62,8 +62,8 @@ pub fn expect_no_body(body: Option<Spanned<&str>>, f: &mut Feedback) {
/// Implement a custom function concisely.
///
/// # Examples
-/// Look at the source code of the [`library`](crate::library) module for
-/// examples on how the macro works.
+/// Look at the source code of the `library` module for examples on how the
+/// macro works.
#[macro_export]
macro_rules! function {
// Entry point.
@@ -85,7 +85,7 @@ macro_rules! function {
// Parse trait.
(@parse($($a:tt)*) parse(default) $($r:tt)*) => {
- function!(@parse($($a)*) parse(_h, _b, _c, _f, _m) {Default::default() } $($r)*);
+ function!(@parse($($a)*) parse(_h, _b, _c, _f, _m) { Default::default() } $($r)*);
};
(@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $f:ident) $($r:tt)* ) => {
function!(@parse($($a)*) parse($h, $b, $c, $f, _metadata) $($r)*);
@@ -104,7 +104,10 @@ macro_rules! function {
#[allow(unused)] mut call: $crate::syntax::parsing::FuncCall,
#[allow(unused)] $state: &$crate::syntax::parsing::ParseState,
#[allow(unused)] $metadata: Self::Meta,
- ) -> $crate::Pass<Self> where Self: Sized {
+ ) -> $crate::Pass<Self>
+ where
+ Self: Sized,
+ {
let mut feedback = $crate::Feedback::new();
#[allow(unused)] let $header = &mut call.header;
#[allow(unused)] let $body = call.body;
diff --git a/src/geom.rs b/src/geom.rs
index 5e65446e..77b1ec11 100644
--- a/src/geom.rs
+++ b/src/geom.rs
@@ -16,32 +16,24 @@ pub struct Value2<T> {
impl<T: Clone> Value2<T> {
/// Create a new 2D-value from two values.
- pub fn new(x: T, y: T) -> Value2<T> { Value2 { x, y } }
+ pub fn new(x: T, y: T) -> Self {
+ Self { x, y }
+ }
/// Create a new 2D-value with `x` set to a value and `y` to default.
- pub fn with_x(x: T) -> Value2<T> where T: Default {
- Value2 { x, y: T::default() }
+ pub fn with_x(x: T) -> Self where T: Default {
+ Self { x, y: T::default() }
}
/// Create a new 2D-value with `y` set to a value and `x` to default.
- pub fn with_y(y: T) -> Value2<T> where T: Default {
- Value2 { x: T::default(), y }
- }
-
- /// Create a new 2D-value with the primary axis set to a value and the other
- /// one to default.
- pub fn with_primary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
- Value2::with_x(v).generalized(axes)
- }
-
- /// Create a new 2D-value with the secondary axis set to a value and the
- /// other one to default.
- pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
- Value2::with_y(v).generalized(axes)
+ pub fn with_y(y: T) -> Self where T: Default {
+ Self { x: T::default(), y }
}
/// Create a 2D-value with `x` and `y` set to the same value `s`.
- pub fn with_all(s: T) -> Value2<T> { Value2 { x: s.clone(), y: s } }
+ pub fn with_all(s: T) -> Self {
+ Self { x: s.clone(), y: s }
+ }
/// Get the specificed component.
pub fn get(self, axis: SpecAxis) -> T {
@@ -83,16 +75,16 @@ impl<T: Clone> Value2<T> {
/// axes, that is:
/// - `x` describes the primary axis instead of the horizontal one.
/// - `y` describes the secondary axis instead of the vertical one.
- pub fn generalized(self, axes: LayoutAxes) -> Value2<T> {
+ pub fn generalized(self, axes: LayoutAxes) -> Self {
match axes.primary.axis() {
Horizontal => self,
- Vertical => Value2 { x: self.y, y: self.x },
+ Vertical => Self { x: self.y, y: self.x },
}
}
/// Returns the specialized version of this generalized Size2D (inverse to
/// `generalized`).
- pub fn specialized(self, axes: LayoutAxes) -> Value2<T> {
+ pub fn specialized(self, axes: LayoutAxes) -> Self {
// In fact, generalized is its own inverse. For reasons of clarity
// at the call site, we still have this second function.
self.generalized(axes)
@@ -104,7 +96,7 @@ impl<T: Clone> Value2<T> {
}
}
-impl<T> Debug for Value2<T> where T: Debug {
+impl<T: Debug> Debug for Value2<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_list()
.entry(&self.x)
@@ -118,16 +110,16 @@ pub type Size = Value2<f64>;
impl Size {
/// The zeroed size.
- pub const ZERO: Size = Size { x: 0.0, y: 0.0 };
+ pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
/// Whether the given size fits into this one, that is, both coordinate
/// values are smaller or equal.
- pub fn fits(self, other: Size) -> bool {
+ pub fn fits(self, other: Self) -> bool {
self.x >= other.x && self.y >= other.y
}
/// Return a size padded by the paddings of the given box.
- pub fn padded(self, padding: Margins) -> Size {
+ pub fn padded(self, padding: Margins) -> Self {
Size {
x: self.x + padding.left + padding.right,
y: self.y + padding.top + padding.bottom,
@@ -135,7 +127,7 @@ impl Size {
}
/// Return a size reduced by the paddings of the given box.
- pub fn unpadded(self, padding: Margins) -> Size {
+ pub fn unpadded(self, padding: Margins) -> Self {
Size {
x: self.x - padding.left - padding.right,
y: self.y - padding.top - padding.bottom,
@@ -147,7 +139,7 @@ impl Size {
///
/// This assumes the size to be generalized such that `x` corresponds to the
/// primary axis.
- pub fn anchor(self, align: LayoutAlign, axes: LayoutAxes) -> Size {
+ pub fn anchor(self, align: LayoutAlign, axes: LayoutAxes) -> Self {
Size {
x: anchor(self.x, align.primary, axes.primary),
y: anchor(self.y, align.secondary, axes.secondary),
@@ -189,17 +181,17 @@ pub struct Value4<T> {
impl<T: Clone> Value4<T> {
/// Create a new box from four sizes.
- pub fn new(left: T, top: T, right: T, bottom: T) -> Value4<T> {
+ pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
Value4 { left, top, right, bottom }
}
/// Create a box with all four fields set to the same value `s`.
- pub fn with_all(value: T) -> Value4<T> {
+ pub fn with_all(value: T) -> Self {
Value4 {
left: value.clone(),
top: value.clone(),
right: value.clone(),
- bottom: value
+ bottom: value,
}
}
@@ -213,7 +205,7 @@ impl<T: Clone> Value4<T> {
}
match dir {
- LTT => &mut self.left,
+ LTR => &mut self.left,
RTL => &mut self.right,
TTB => &mut self.top,
BTT => &mut self.bottom,
@@ -224,18 +216,6 @@ impl<T: Clone> Value4<T> {
pub fn set_all(&mut self, value: T) {
*self = Value4::with_all(value);
}
-
- /// Set the `left` and `right` values.
- pub fn set_horizontal(&mut self, value: T) {
- self.left = value.clone();
- self.right = value;
- }
-
- /// Set the `top` and `bottom` values.
- pub fn set_vertical(&mut self, value: T) {
- self.top = value.clone();
- self.bottom = value;
- }
}
/// A length in four dimensions.
diff --git a/src/layout/elements.rs b/src/layout/elements.rs
index 92b53ae8..b4a6b31c 100644
--- a/src/layout/elements.rs
+++ b/src/layout/elements.rs
@@ -1,19 +1,20 @@
-//! The elements layouts are composed of.
+//! Basic building blocks of layouts.
use std::fmt::{self, Debug, Formatter};
-use ttf_parser::GlyphId;
use fontdock::FaceId;
+use ttf_parser::GlyphId;
+
use crate::geom::Size;
-/// A sequence of positioned layout elements.
-#[derive(Debug, Clone, PartialEq)]
+/// A collection of absolutely positioned layout elements.
+#[derive(Debug, Default, Clone, PartialEq)]
pub struct LayoutElements(pub Vec<(Size, LayoutElement)>);
impl LayoutElements {
- /// Create an empty sequence.
+ /// Create an new empty collection.
pub fn new() -> Self {
- LayoutElements(vec![])
+ Self(vec![])
}
/// Add an element at a position.
@@ -21,7 +22,9 @@ impl LayoutElements {
self.0.push((pos, element));
}
- /// Add a sequence of elements offset by an `offset`.
+ /// Add all elements of another collection, offsetting each by the given
+ /// `offset`. This can be used to place a sublayout at a position in another
+ /// layout.
pub fn extend_offset(&mut self, offset: Size, more: Self) {
for (subpos, element) in more.0 {
self.0.push((subpos + offset, element));
@@ -29,16 +32,9 @@ impl LayoutElements {
}
}
-impl Default for LayoutElements {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// A layout element, which is the basic building block layouts are composed of.
+/// A layout element, the basic building block layouts are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum LayoutElement {
- /// Shaped text.
Text(Shaped),
}
@@ -48,14 +44,17 @@ pub struct Shaped {
pub text: String,
pub face: FaceId,
pub glyphs: Vec<GlyphId>,
+ /// The horizontal offsets of the glyphs with the same indices. Vertical
+ /// offets are not yet supported.
pub offsets: Vec<f64>,
+ /// The font size.
pub size: f64,
}
impl Shaped {
- /// Create an empty shape run.
- pub fn new(face: FaceId, size: f64) -> Shaped {
- Shaped {
+ /// Create a new shape run with empty `text`, `glyphs` and `offsets`.
+ pub fn new(face: FaceId, size: f64) -> Self {
+ Self {
text: String::new(),
face,
glyphs: vec![],
@@ -65,12 +64,11 @@ impl Shaped {
}
/// Encode the glyph ids into a big-endian byte buffer.
- pub fn encode_glyphs(&self) -> Vec<u8> {
- const BYTES_PER_GLYPH: usize = 2;
- let mut bytes = Vec::with_capacity(BYTES_PER_GLYPH * self.glyphs.len());
- for g in &self.glyphs {
- bytes.push((g.0 >> 8) as u8);
- bytes.push((g.0 & 0xff) as u8);
+ pub fn encode_glyphs_be(&self) -> Vec<u8> {
+ let mut bytes = Vec::with_capacity(2 * self.glyphs.len());
+ for &GlyphId(g) in &self.glyphs {
+ bytes.push((g >> 8) as u8);
+ bytes.push((g & 0xff) as u8);
}
bytes
}
diff --git a/src/layout/line.rs b/src/layout/line.rs
index 358d2ac9..069a4e56 100644
--- a/src/layout/line.rs
+++ b/src/layout/line.rs
@@ -1,70 +1,67 @@
-//! The line layouter arranges boxes into lines.
+//! Arranging boxes into lines.
//!
-//! Along the primary axis, the boxes are laid out next to each other while they
-//! fit into a line. When a line break is necessary, the line is finished and a
-//! new line is started offset on the secondary axis by the height of previous
-//! line and the extra line spacing.
+//! Along the primary axis, the boxes are laid out next to each other as long as
+//! they fit into a line. When necessary, a line break is inserted and the new
+//! line is offset along the secondary axis by the height of the previous line
+//! plus extra line spacing.
//!
-//! Internally, the line layouter uses a stack layouter to arrange the finished
-//! lines.
+//! Internally, the line layouter uses a stack layouter to stack the finished
+//! lines on top of each.
-use super::stack::{StackLayouter, StackContext};
+use super::stack::{StackContext, StackLayouter};
use super::*;
/// Performs the line layouting.
-#[derive(Debug)]
pub struct LineLayouter {
- /// The context for layouting.
ctx: LineContext,
- /// The underlying stack layouter.
stack: StackLayouter,
- /// The currently written line.
+ /// The in-progress line.
run: LineRun,
}
/// The context for line layouting.
#[derive(Debug, Clone)]
pub struct LineContext {
- /// The spaces to layout in.
+ /// The spaces to layout into.
pub spaces: LayoutSpaces,
- /// The initial layouting axes, which can be updated by the
- /// [`LineLayouter::set_axes`] method.
+ /// The initial layouting axes, which can be updated through `set_axes`.
pub axes: LayoutAxes,
- /// Which alignment to set on the resulting layout. This affects how it will
- /// be positioned in a parent box.
+ /// The alignment of the _resulting_ layout. This does not effect the line
+ /// layouting itself, but rather how the finished layout will be positioned
+ /// in a parent layout.
pub align: LayoutAlign,
- /// Whether to have repeated spaces or to use only the first and only once.
+ /// Whether to spill over into copies of the last space or finish layouting
+ /// when the last space is used up.
pub repeat: bool,
- /// The line spacing.
+ /// The spacing to be inserted between each pair of lines.
pub line_spacing: f64,
}
-/// A line run is a sequence of boxes with the same alignment that are arranged
-/// in a line. A real line can consist of multiple runs with different
-/// alignments.
-#[derive(Debug)]
+/// A sequence of boxes with the same alignment. A real line can consist of
+/// multiple runs with different alignments.
struct LineRun {
- /// The so-far accumulated layouts in the line.
+ /// The so-far accumulated items of the run.
layouts: Vec<(f64, BoxLayout)>,
- /// The width and maximal height of the line.
+ /// The summed width and maximal height of the run.
size: Size,
/// The alignment of all layouts in the line.
///
- /// When a new run is created the alignment is yet to be determined. Once a
- /// layout is added, it is decided which alignment the run has and all
- /// further elements of the run must have this alignment.
+ /// When a new run is created the alignment is yet to be determined and
+ /// `None` as such. Once a layout is added, its alignment decides the
+ /// alignment for the whole run.
align: Option<LayoutAlign>,
- /// If another line run with different alignment already took up some space
- /// of the line, this run has less space and how much is stored here.
+ /// The amount of space left by another run on the same line or `None` if
+ /// this is the only run so far.
usable: Option<f64>,
- /// A possibly cached soft spacing or spacing state.
+ /// The spacing state. This influences how new spacing is handled, e.g. hard
+ /// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl LineLayouter {
/// Create a new line layouter.
- pub fn new(ctx: LineContext) -> LineLayouter {
- LineLayouter {
+ pub fn new(ctx: LineContext) -> Self {
+ Self {
stack: StackLayouter::new(StackContext {
spaces: ctx.spaces.clone(),
axes: ctx.axes,
@@ -76,14 +73,14 @@ impl LineLayouter {
}
}
- /// Add a layout to the run.
+ /// Add a layout.
pub fn add(&mut self, layout: BoxLayout) {
let axes = self.ctx.axes;
if let Some(align) = self.run.align {
if layout.align.secondary != align.secondary {
// TODO: Issue warning for non-fitting alignment in
- // non-repeating context.
+ // non-repeating context.
let fitting = self.stack.is_fitting_alignment(layout.align);
if !fitting && self.ctx.repeat {
self.finish_space(true);
@@ -92,7 +89,6 @@ impl LineLayouter {
}
} else if layout.align.primary < align.primary {
self.finish_line();
-
} else if layout.align.primary > align.primary {
let mut rest_run = LineRun::new();
@@ -137,25 +133,24 @@ impl LineLayouter {
self.run.last_spacing = LastSpacing::None;
}
- /// Add multiple layouts to the run.
+ /// Add multiple layouts.
///
- /// This function simply calls `add` repeatedly for each layout.
+ /// This is equivalent to calling `add` repeatedly for each layout.
pub fn add_multiple(&mut self, layouts: MultiLayout) {
for layout in layouts {
self.add(layout);
}
}
- /// The remaining usable size of the run.
+ /// The remaining usable size of the line.
///
- /// This specifies how much more fits before a line break needs to be
- /// issued.
+ /// This specifies how much more would fit before a line break would be
+ /// needed.
fn usable(&self) -> Size {
- // The base is the usable space per stack layouter.
+ // The base is the usable space of the stack layouter.
let mut usable = self.stack.usable().generalized(self.ctx.axes);
- // If this is a alignment-continuing line, we override the primary
- // usable size.
+ // If there was another run already, override the stack's size.
if let Some(primary) = self.run.usable {
usable.x = primary;
}
@@ -164,18 +159,17 @@ impl LineLayouter {
usable
}
- /// Add spacing along the primary axis to the line.
+ /// Add spacing to the line.
pub fn add_primary_spacing(&mut self, mut spacing: f64, kind: SpacingKind) {
match kind {
- // A hard space is simply an empty box.
SpacingKind::Hard => {
spacing = spacing.min(self.usable().x);
self.run.size.x += spacing;
self.run.last_spacing = LastSpacing::Hard;
}
- // A soft space is cached if it is not consumed by a hard space or
- // previous soft space with higher level.
+ // A soft space is cached since it might be consumed by a hard
+ // spacing.
SpacingKind::Soft(level) => {
let consumes = match self.run.last_spacing {
LastSpacing::None => true,
@@ -190,23 +184,23 @@ impl LineLayouter {
}
}
- /// Finish the line and add secondary spacing to the underlying stack.
+ /// Finish the line and add spacing to the underlying stack.
pub fn add_secondary_spacing(&mut self, spacing: f64, kind: SpacingKind) {
self.finish_line_if_not_empty();
self.stack.add_spacing(spacing, kind)
}
- /// Update the layouting axes used by this layouter.
+ /// Update the layouting axes.
pub fn set_axes(&mut self, axes: LayoutAxes) {
self.finish_line_if_not_empty();
self.ctx.axes = axes;
self.stack.set_axes(axes)
}
- /// Update the layouting spaces to use.
+ /// Update the layouting spaces.
///
/// If `replace_empty` is true, the current space is replaced if there are
- /// no boxes laid into it yet. Otherwise, only the followup spaces are
+ /// no boxes laid out into it yet. Otherwise, the followup spaces are
/// replaced.
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
self.stack.set_spaces(spaces, replace_empty && self.line_is_empty());
@@ -217,13 +211,11 @@ impl LineLayouter {
self.ctx.line_spacing = line_spacing;
}
- /// The remaining inner layout spaces. Inner means, that padding is already
- /// subtracted and the spaces are unexpanding. This can be used to signal
- /// a function how much space it has to layout itself.
+ /// The remaining inner spaces. If something is laid out into these spaces,
+ /// it will fit into this layouter's underlying stack.
pub fn remaining(&self) -> LayoutSpaces {
let mut spaces = self.stack.remaining();
- *spaces[0].size.secondary_mut(self.ctx.axes)
- -= self.run.size.y;
+ *spaces[0].size.secondary_mut(self.ctx.axes) -= self.run.size.y;
spaces
}
@@ -232,13 +224,13 @@ impl LineLayouter {
self.run.size == Size::ZERO && self.run.layouts.is_empty()
}
- /// Finish the last line and compute the final list of boxes.
+ /// Finish everything up and return the final collection of boxes.
pub fn finish(mut self) -> MultiLayout {
self.finish_line_if_not_empty();
self.stack.finish()
}
- /// Finish the currently active space and start a new one.
+ /// Finish the active space and start a new one.
///
/// At the top level, this is a page break.
pub fn finish_space(&mut self, hard: bool) {
@@ -246,7 +238,7 @@ impl LineLayouter {
self.stack.finish_space(hard)
}
- /// Finish the line and start a new one.
+ /// Finish the active line and start a new one.
pub fn finish_line(&mut self) {
let mut elements = LayoutElements::new();
@@ -265,9 +257,8 @@ impl LineLayouter {
self.stack.add(BoxLayout {
size: self.run.size.specialized(self.ctx.axes),
- align: self.run.align
- .unwrap_or(LayoutAlign::new(Start, Start)),
- elements
+ align: self.run.align.unwrap_or(LayoutAlign::new(Start, Start)),
+ elements
});
self.run = LineRun::new();
@@ -275,7 +266,6 @@ impl LineLayouter {
self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE);
}
- /// Finish the current line if it is not empty.
fn finish_line_if_not_empty(&mut self) {
if !self.line_is_empty() {
self.finish_line()
@@ -284,8 +274,8 @@ impl LineLayouter {
}
impl LineRun {
- fn new() -> LineRun {
- LineRun {
+ fn new() -> Self {
+ Self {
layouts: vec![],
size: Size::ZERO,
align: None,
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 143f1984..8a68a6a5 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -1,37 +1,37 @@
-//! Layouting types and engines.
-
-use async_trait::async_trait;
-
-use crate::Pass;
-use crate::font::SharedFontLoader;
-use crate::geom::{Size, Margins};
-use crate::style::{LayoutStyle, TextStyle, PageStyle};
-use crate::syntax::tree::SyntaxTree;
-
-use elements::LayoutElements;
-use tree::TreeLayouter;
-use prelude::*;
+//! Layouting of syntax trees into box layouts.
pub mod elements;
pub mod line;
pub mod primitive;
pub mod stack;
pub mod text;
-pub mod tree;
-
-pub use primitive::*;
+mod tree;
/// Basic types used across the layouting engine.
pub mod prelude {
- pub use super::layout;
pub use super::primitive::*;
+ pub use super::layout;
pub use Dir::*;
- pub use GenAxis::*;
- pub use SpecAxis::*;
pub use GenAlign::*;
+ pub use GenAxis::*;
pub use SpecAlign::*;
+ pub use SpecAxis::*;
}
+pub use primitive::*;
+pub use tree::layout_tree as layout;
+
+use async_trait::async_trait;
+
+use crate::font::SharedFontLoader;
+use crate::geom::{Margins, Size};
+use crate::style::{LayoutStyle, PageStyle, TextStyle};
+use crate::syntax::tree::SyntaxTree;
+use crate::Pass;
+
+use elements::LayoutElements;
+use prelude::*;
+
/// A collection of layouts.
pub type MultiLayout = Vec<BoxLayout>;
@@ -40,47 +40,41 @@ pub type MultiLayout = Vec<BoxLayout>;
pub struct BoxLayout {
/// The size of the box.
pub size: Size,
- /// How to align this layout in a parent container.
+ /// How to align this box in a parent container.
pub align: LayoutAlign,
/// The elements composing this layout.
pub elements: LayoutElements,
}
-/// Layouting of elements.
+/// Comamnd-based layout.
#[async_trait(?Send)]
pub trait Layout {
- /// Layout self into a sequence of layouting commands.
- async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>>;
-}
-
-/// Layout a syntax tree into a list of boxes.
-pub async fn layout(tree: &SyntaxTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
- let mut layouter = TreeLayouter::new(ctx);
- layouter.layout_tree(tree).await;
- layouter.finish()
+ /// Create a sequence of layouting commands to execute.
+ async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
}
/// The context for layouting.
#[derive(Debug, Clone)]
pub struct LayoutContext<'a> {
- /// The font loader to retrieve fonts from when typesetting text
- /// using [`layout_text`].
+ /// The font loader to query fonts from when typesetting text.
pub loader: &'a SharedFontLoader,
/// The style for pages and text.
pub style: &'a LayoutStyle,
- /// The base unpadded size of this container (for relative sizing).
+ /// The unpadded size of this container (the base 100% for relative sizes).
pub base: Size,
- /// The spaces to layout in.
+ /// The spaces to layout into.
pub spaces: LayoutSpaces,
- /// Whether to have repeated spaces or to use only the first and only once.
+ /// Whether to spill over into copies of the last space or finish layouting
+ /// when the last space is used up.
pub repeat: bool,
- /// The initial axes along which content is laid out.
+ /// The axes along which content is laid out.
pub axes: LayoutAxes,
- /// The alignment of the finished layout.
+ /// The alignment of the _resulting_ layout. This does not effect the line
+ /// layouting itself, but rather how the finished layout will be positioned
+ /// in a parent layout.
pub align: LayoutAlign,
- /// Whether the layout that is to be created will be nested in a parent
- /// container.
- pub nested: bool,
+ /// Whether this layouting process is the root page-building process.
+ pub root: bool,
}
/// A collection of layout spaces.
@@ -89,17 +83,17 @@ pub type LayoutSpaces = Vec<LayoutSpace>;
/// The space into which content is laid out.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LayoutSpace {
- /// The maximum size of the box to layout in.
+ /// The maximum size of the rectangle to layout into.
pub size: Size,
/// Padding that should be respected on each side.
pub padding: Margins,
/// Whether to expand the size of the resulting layout to the full size of
- /// this space or to shrink them to fit the content.
+ /// this space or to shrink it to fit the content.
pub expansion: LayoutExpansion,
}
impl LayoutSpace {
- /// The offset from the origin to the start of content, that is,
+ /// The offset from the origin to the start of content, i.e.
/// `(padding.left, padding.top)`.
pub fn start(&self) -> Size {
Size::new(self.padding.left, self.padding.top)
@@ -110,9 +104,10 @@ impl LayoutSpace {
self.size.unpadded(self.padding)
}
- /// A layout space without padding and size reduced by the padding.
- pub fn usable_space(&self) -> LayoutSpace {
- LayoutSpace {
+ /// The inner layout space with size reduced by the padding, zero padding of
+ /// its own and no layout expansion.
+ pub fn inner(&self) -> Self {
+ Self {
size: self.usable(),
padding: Margins::ZERO,
expansion: LayoutExpansion::new(false, false),
@@ -123,34 +118,33 @@ impl LayoutSpace {
/// A sequence of layouting commands.
pub type Commands<'a> = Vec<Command<'a>>;
-/// Commands issued to the layouting engine by trees.
+/// Commands executable by the layouting engine.
#[derive(Debug, Clone)]
pub enum Command<'a> {
/// Layout the given tree in the current context (i.e. not nested). The
/// content of the tree is not laid out into a separate box and then added,
- /// but simply laid out flat in the active layouting process.
+ /// but simply laid out flatly in the active layouting process.
///
/// This has the effect that the content fits nicely into the active line
/// layouting, enabling functions to e.g. change the style of some piece of
- /// text while keeping it integrated in the current paragraph.
+ /// text while keeping it part of the current paragraph.
LayoutSyntaxTree(&'a SyntaxTree),
- /// Add a already computed layout.
+ /// Add a finished layout.
Add(BoxLayout),
/// Add multiple layouts, one after another. This is equivalent to multiple
- /// [Add](Command::Add) commands.
+ /// `Add` commands.
AddMultiple(MultiLayout),
- /// Add spacing of given [kind](super::SpacingKind) along the primary or
- /// secondary axis. The spacing kind defines how the spacing interacts with
- /// surrounding spacing.
+ /// Add spacing of the given kind along the primary or secondary axis. The
+ /// kind defines how the spacing interacts with surrounding spacing.
AddSpacing(f64, SpacingKind, GenAxis),
/// Start a new line.
BreakLine,
/// Start a new paragraph.
BreakParagraph,
- /// Start a new page, which will exist in the finished layout even if it
+ /// Start a new page, which will be part of the finished layout even if it
/// stays empty (since the page break is a _hard_ space break).
BreakPage,
@@ -162,6 +156,6 @@ pub enum Command<'a> {
/// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign),
/// Update the layouting axes along which future boxes will be laid
- /// out. This finishes the current line.
+ /// out. This ends the current line.
SetAxes(LayoutAxes),
}
diff --git a/src/layout/primitive.rs b/src/layout/primitive.rs
index 2eb5669b..1d79f530 100644
--- a/src/layout/primitive.rs
+++ b/src/layout/primitive.rs
@@ -1,29 +1,27 @@
//! Layouting primitives.
use std::fmt::{self, Display, Formatter};
+
use super::prelude::*;
-/// Specifies along which axes content is laid out.
+/// Specifies the axes along content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAxes {
- /// The primary layouting direction.
pub primary: Dir,
- /// The secondary layouting direction.
pub secondary: Dir,
}
impl LayoutAxes {
- /// Create a new instance from the two values.
+ /// Create a new instance from the two directions.
///
/// # Panics
- /// This function panics if the axes are aligned, that is, they are
+ /// This function panics if the directions are aligned, i.e. if they are
/// on the same axis.
- pub fn new(primary: Dir, secondary: Dir) -> LayoutAxes {
+ pub fn new(primary: Dir, secondary: Dir) -> Self {
if primary.axis() == secondary.axis() {
- panic!("invalid aligned axes {} and {}", primary, secondary);
+ panic!("directions {} and {} are aligned", primary, secondary);
}
-
- LayoutAxes { primary, secondary }
+ Self { primary, secondary }
}
/// Return the direction of the specified generic axis.
@@ -46,9 +44,13 @@ impl LayoutAxes {
/// Directions along which content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir {
- LTT,
+ /// Left to right.
+ LTR,
+ /// Right to left.
RTL,
+ /// Top to bottom.
TTB,
+ /// Bottom to top.
BTT,
}
@@ -56,34 +58,34 @@ impl Dir {
/// The specific axis this direction belongs to.
pub fn axis(self) -> SpecAxis {
match self {
- LTT | RTL => Horizontal,
+ LTR | RTL => Horizontal,
TTB | BTT => Vertical,
}
}
- /// Whether this axis points into the positive coordinate direction.
+ /// Whether this direction points into the positive coordinate direction.
///
- /// The positive axes are left-to-right and top-to-bottom.
+ /// The positive directions are left-to-right and top-to-bottom.
pub fn is_positive(self) -> bool {
match self {
- LTT | TTB => true,
+ LTR | TTB => true,
RTL | BTT => false,
}
}
/// The factor for this direction.
///
- /// - `1` if the direction is positive.
- /// - `-1` if the direction is negative.
+ /// - `1.0` if the direction is positive.
+ /// - `-1.0` if the direction is negative.
pub fn factor(self) -> f64 {
if self.is_positive() { 1.0 } else { -1.0 }
}
- /// The inverse axis.
- pub fn inv(self) -> Dir {
+ /// The inverse direction.
+ pub fn inv(self) -> Self {
match self {
- LTT => RTL,
- RTL => LTT,
+ LTR => RTL,
+ RTL => LTR,
TTB => BTT,
BTT => TTB,
}
@@ -93,7 +95,7 @@ impl Dir {
impl Display for Dir {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
- LTT => "ltr",
+ LTR => "ltr",
RTL => "rtl",
TTB => "ttb",
BTT => "btt",
@@ -104,9 +106,9 @@ impl Display for Dir {
/// The two generic layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenAxis {
- /// The primary axis along which words are laid out.
+ /// The primary layouting direction along which text and lines flow.
Primary,
- /// The secondary axis along which lines and paragraphs are laid out.
+ /// The secondary layouting direction along which paragraphs grow.
Secondary,
}
@@ -154,19 +156,17 @@ impl Display for SpecAxis {
/// Specifies where to align a layout in a parent container.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAlign {
- /// The alignment along the primary axis.
pub primary: GenAlign,
- /// The alignment along the secondary axis.
pub secondary: GenAlign,
}
impl LayoutAlign {
- /// Create a new instance from the two values.
- pub fn new(primary: GenAlign, secondary: GenAlign) -> LayoutAlign {
- LayoutAlign { primary, secondary }
+ /// Create a new instance from the two alignments.
+ pub fn new(primary: GenAlign, secondary: GenAlign) -> Self {
+ Self { primary, secondary }
}
- /// Return the alignment of the specified generic axis.
+ /// Return the alignment for the specified generic axis.
pub fn get(self, axis: GenAxis) -> GenAlign {
match axis {
Primary => self.primary,
@@ -174,7 +174,7 @@ impl LayoutAlign {
}
}
- /// Borrow the alignment of the specified generic axis mutably.
+ /// Borrow the alignment for the specified generic axis mutably.
pub fn get_mut(&mut self, axis: GenAxis) -> &mut GenAlign {
match axis {
Primary => &mut self.primary,
@@ -183,7 +183,7 @@ impl LayoutAlign {
}
}
-/// Where to align content along a generic context.
+/// Where to align content along an axis in a generic context.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GenAlign {
Start,
@@ -193,7 +193,7 @@ pub enum GenAlign {
impl GenAlign {
/// The inverse alignment.
- pub fn inv(self) -> GenAlign {
+ pub fn inv(self) -> Self {
match self {
Start => End,
Center => Center,
@@ -212,7 +212,7 @@ impl Display for GenAlign {
}
}
-/// Where to align content in a specific context.
+/// Where to align content along an axis in a specific context.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum SpecAlign {
Left,
@@ -225,7 +225,7 @@ pub enum SpecAlign {
impl SpecAlign {
/// The specific axis this alignment refers to.
///
- /// Returns `None` if this is center.
+ /// Returns `None` if this is `Center` since the axis is unknown.
pub fn axis(self) -> Option<SpecAxis> {
match self {
Self::Left => Some(Horizontal),
@@ -236,7 +236,7 @@ impl SpecAlign {
}
}
- /// Convert this to a generic alignment.
+ /// The generic version of this alignment in the given system of axes.
pub fn to_generic(self, axes: LayoutAxes) -> GenAlign {
let get = |spec: SpecAxis, align: GenAlign| {
let axis = spec.to_generic(axes);
@@ -277,8 +277,8 @@ pub struct LayoutExpansion {
impl LayoutExpansion {
/// Create a new instance from the two values.
- pub fn new(horizontal: bool, vertical: bool) -> LayoutExpansion {
- LayoutExpansion { horizontal, vertical }
+ pub fn new(horizontal: bool, vertical: bool) -> Self {
+ Self { horizontal, vertical }
}
/// Return the expansion value for the given specific axis.
@@ -298,8 +298,7 @@ impl LayoutExpansion {
}
}
-/// Defines how a given spacing interacts with (possibly existing) surrounding
-/// spacing.
+/// Defines how spacing interacts with surrounding spacing.
///
/// There are two options for interaction: Hard and soft spacing. Typically,
/// hard spacing is used when a fixed amount of space needs to be inserted no
@@ -317,31 +316,31 @@ pub enum SpacingKind {
impl SpacingKind {
/// The standard spacing kind used for paragraph spacing.
- pub const PARAGRAPH: SpacingKind = SpacingKind::Soft(1);
+ pub const PARAGRAPH: Self = Self::Soft(1);
/// The standard spacing kind used for line spacing.
- pub const LINE: SpacingKind = SpacingKind::Soft(2);
+ pub const LINE: Self = Self::Soft(2);
/// The standard spacing kind used for word spacing.
- pub const WORD: SpacingKind = SpacingKind::Soft(1);
+ pub const WORD: Self = Self::Soft(1);
}
/// The spacing kind of the most recently inserted item in a layouting process.
-/// This is not about the last _spacing item_, but the last _item_, which is why
-/// this can be `None`.
+///
+/// Since the last inserted item may not be spacing at all, this can be `None`.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum LastSpacing {
/// The last item was hard spacing.
Hard,
/// The last item was soft spacing with the given width and level.
Soft(f64, u32),
- /// The last item was not spacing.
+ /// The last item wasn't spacing.
None,
}
impl LastSpacing {
/// The width of the soft space if this is a soft space or zero otherwise.
- pub(crate) fn soft_or_zero(self) -> f64 {
+ pub fn soft_or_zero(self) -> f64 {
match self {
LastSpacing::Soft(space, _) => space,
_ => 0.0,
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 28da74b7..62f2c976 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,11 +1,9 @@
-//! The stack layouter arranges boxes along the secondary layouting axis.
+//! Arranging boxes into a stack along the secondary axis.
//!
-//! Individual layouts can be aligned at origin / center / end on both axes and
-//! these alignments are with respect to the growable layout space and not the
-//! total possible size.
-//!
-//! This means that a later layout can have influence on the position of an
-//! earlier one. Consider, for example, the following code:
+//! Individual layouts can be aligned at `Start`, `Center` or `End` along both
+//! axes. These alignments are with respect to the size of the finished layout
+//! and not the total usable size. This means that a later layout can have
+//! influence on the position of an earlier one. Consider the following example.
//! ```typst
//! [align: right][A word.]
//! [align: left][A sentence with a couple more words.]
@@ -25,38 +23,35 @@ use crate::geom::Value4;
use super::*;
/// Performs the stack layouting.
-#[derive(Debug)]
pub struct StackLayouter {
- /// The context for layouting.
ctx: StackContext,
- /// The output layouts.
layouts: MultiLayout,
- /// The currently active layout space.
+ /// The in-progress space.
space: Space,
}
/// The context for stack layouting.
#[derive(Debug, Clone)]
pub struct StackContext {
- /// The spaces to layout in.
+ /// The spaces to layout into.
pub spaces: LayoutSpaces,
- /// The initial layouting axes, which can be updated by the
- /// [`StackLayouter::set_axes`] method.
+ /// The initial layouting axes, which can be updated through `set_axes`.
pub axes: LayoutAxes,
- /// Which alignment to set on the resulting layout. This affects how it will
- /// be positioned in a parent box.
+ /// The alignment of the _resulting_ layout. This does not effect the line
+ /// layouting itself, but rather how the finished layout will be positioned
+ /// in a parent layout.
pub align: LayoutAlign,
- /// Whether to have repeated spaces or to use only the first and only once.
+ /// Whether to spill over into copies of the last space or finish layouting
+ /// when the last space is used up.
pub repeat: bool,
}
/// A layout space composed of subspaces which can have different axes and
/// alignments.
-#[derive(Debug)]
struct Space {
- /// The index of this space in the list of spaces.
+ /// The index of this space in `ctx.spaces`.
index: usize,
- /// Whether to add the layout for this space even if it would be empty.
+ /// Whether to include a layout for this space even if it would be empty.
hard: bool,
/// The so-far accumulated layouts.
layouts: Vec<(LayoutAxes, BoxLayout)>,
@@ -66,18 +61,20 @@ struct Space {
usable: Size,
/// The specialized extra-needed size to affect the size at all.
extra: Size,
- /// The rulers of a space dictate which alignments for new boxes are still
- /// allowed and which require a new space to be started.
+ /// Dictate which alignments for new boxes are still allowed and which
+ /// require a new space to be started. For example, after an `End`-aligned
+ /// item, no `Start`-aligned one can follow.
rulers: Value4<GenAlign>,
- /// The last added spacing if the last added thing was spacing.
+ /// The spacing state. This influences how new spacing is handled, e.g. hard
+ /// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl StackLayouter {
/// Create a new stack layouter.
- pub fn new(ctx: StackContext) -> StackLayouter {
+ pub fn new(ctx: StackContext) -> Self {
let space = ctx.spaces[0];
- StackLayouter {
+ Self {
ctx,
layouts: MultiLayout::new(),
space: Space::new(0, true, space.usable()),
@@ -87,8 +84,8 @@ impl StackLayouter {
/// Add a layout to the stack.
pub fn add(&mut self, layout: BoxLayout) {
// If the alignment cannot be fitted in this space, finish it.
- // TODO: Issue warning for non-fitting alignment in
- // non-repeating context.
+ // TODO: Issue warning for non-fitting alignment in non-repeating
+ // context.
if !self.update_rulers(layout.align) && self.ctx.repeat {
self.finish_space(true);
}
@@ -116,14 +113,14 @@ impl StackLayouter {
/// Add multiple layouts to the stack.
///
- /// This function simply calls `add` repeatedly for each layout.
+ /// This is equivalent to calling `add` repeatedly for each layout.
pub fn add_multiple(&mut self, layouts: MultiLayout) {
for layout in layouts {
self.add(layout);
}
}
- /// Add secondary spacing to the stack.
+ /// Add spacing to the stack.
pub fn add_spacing(&mut self, mut spacing: f64, kind: SpacingKind) {
match kind {
// A hard space is simply an empty box.
@@ -133,11 +130,14 @@ impl StackLayouter {
let size = Size::with_y(spacing);
self.update_metrics(size);
- self.space.layouts.push((self.ctx.axes, BoxLayout {
- size: size.specialized(self.ctx.axes),
- align: LayoutAlign::new(Start, Start),
- elements: LayoutElements::new(),
- }));
+ self.space.layouts.push((
+ self.ctx.axes,
+ BoxLayout {
+ size: size.specialized(self.ctx.axes),
+ align: LayoutAlign::new(Start, Start),
+ elements: LayoutElements::new(),
+ }
+ ));
self.space.last_spacing = LastSpacing::Hard;
}
@@ -158,8 +158,6 @@ impl StackLayouter {
}
}
- /// Update the size metrics to reflect that a layout or spacing with the
- /// given generalized size has been added.
fn update_metrics(&mut self, added: Size) {
let axes = self.ctx.axes;
@@ -177,31 +175,29 @@ impl StackLayouter {
*self.space.usable.secondary_mut(axes) -= added.y;
}
- /// Update the rulers to account for the new layout. Returns true if a
- /// space break is necessary.
+ /// Returns true if a space break is necessary.
fn update_rulers(&mut self, align: LayoutAlign) -> bool {
let allowed = self.is_fitting_alignment(align);
if allowed {
- *self.space.rulers.get_mut(self.ctx.axes.secondary, Start)
- = align.secondary;
+ *self.space.rulers.get_mut(self.ctx.axes.secondary, Start) =
+ align.secondary;
}
allowed
}
- /// Whether a layout with the given alignment can still be layouted in the
- /// active space.
- pub fn is_fitting_alignment(&mut self, align: LayoutAlign) -> bool {
+ /// Whether a layout with the given alignment can still be layouted into the
+ /// active space or a space break is necessary.
+ pub(crate) fn is_fitting_alignment(&mut self, align: LayoutAlign) -> bool {
self.is_fitting_axis(self.ctx.axes.primary, align.primary)
&& self.is_fitting_axis(self.ctx.axes.secondary, align.secondary)
}
- /// Whether the given alignment is still allowed according to the rulers.
fn is_fitting_axis(&mut self, dir: Dir, align: GenAlign) -> bool {
align >= *self.space.rulers.get_mut(dir, Start)
- && align <= self.space.rulers.get_mut(dir, End).inv()
+ && align <= self.space.rulers.get_mut(dir, End).inv()
}
- /// Change the layouting axes used by this layouter.
+ /// Update the layouting axes.
pub fn set_axes(&mut self, axes: LayoutAxes) {
// Forget the spacing because it is not relevant anymore.
if axes.secondary != self.ctx.axes.secondary {
@@ -211,10 +207,10 @@ impl StackLayouter {
self.ctx.axes = axes;
}
- /// Change the layouting spaces to use.
+ /// Update the layouting spaces.
///
/// If `replace_empty` is true, the current space is replaced if there are
- /// no boxes laid into it yet. Otherwise, only the followup spaces are
+ /// no boxes laid out into it yet. Otherwise, the followup spaces are
/// replaced.
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
if replace_empty && self.space_is_empty() {
@@ -234,13 +230,13 @@ impl StackLayouter {
if space.usable().fits(size) {
self.finish_space(true);
self.start_space(start + index, true);
- return;
+ break;
}
}
}
- /// The remaining unpadded, unexpanding spaces. If a function is laid out
- /// into these spaces, it will fit into this stack.
+ /// The remaining inner spaces. If something is laid out into these spaces,
+ /// it will fit into this stack.
pub fn remaining(&self) -> LayoutSpaces {
let size = self.usable();
@@ -251,7 +247,7 @@ impl StackLayouter {
}];
for space in &self.ctx.spaces[self.next_space()..] {
- spaces.push(space.usable_space());
+ spaces.push(space.inner());
}
spaces
@@ -264,17 +260,17 @@ impl StackLayouter {
.specialized(self.ctx.axes)
}
- /// Whether the current layout space (not subspace) is empty.
+ /// Whether the current layout space is empty.
pub fn space_is_empty(&self) -> bool {
self.space.size == Size::ZERO && self.space.layouts.is_empty()
}
- /// Whether the current layout space is the last is the followup list.
+ /// Whether the current layout space is the last in the followup list.
pub fn space_is_last(&self) -> bool {
self.space.index == self.ctx.spaces.len() - 1
}
- /// Compute the finished list of boxes.
+ /// Finish everything up and return the final collection of boxes.
pub fn finish(mut self) -> MultiLayout {
if self.space.hard || !self.space_is_empty() {
self.finish_space(false);
@@ -282,7 +278,7 @@ impl StackLayouter {
self.layouts
}
- /// Finish the current space and start a new one.
+ /// Finish active current space and start a new one.
pub fn finish_space(&mut self, hard: bool) {
let space = self.ctx.spaces[self.space.index];
@@ -322,8 +318,8 @@ impl StackLayouter {
// layout uses up space from the origin to the end. Thus, it reduces
// the usable space for following layouts at it's origin by its
// extent along the secondary axis.
- *bound.get_mut(axes.secondary, Start)
- += axes.secondary.factor() * layout.size.secondary(*axes);
+ *bound.get_mut(axes.secondary, Start) +=
+ axes.secondary.factor() * layout.size.secondary(*axes);
}
// ------------------------------------------------------------------ //
@@ -351,8 +347,8 @@ impl StackLayouter {
// We reduce the bounding box of this layout at it's end by the
// accumulated secondary extent of all layouts we have seen so far,
// which are the layouts after this one since we iterate reversed.
- *bound.get_mut(axes.secondary, End)
- -= axes.secondary.factor() * extent.y;
+ *bound.get_mut(axes.secondary, End) -=
+ axes.secondary.factor() * extent.y;
// Then, we add this layout's secondary extent to the accumulator.
let size = layout.size.generalized(*axes);
@@ -395,21 +391,19 @@ impl StackLayouter {
self.start_space(self.next_space(), hard)
}
- /// Start a new space with the given index.
fn start_space(&mut self, index: usize, hard: bool) {
let space = self.ctx.spaces[index];
self.space = Space::new(index, hard, space.usable());
}
- /// The index of the next space.
fn next_space(&self) -> usize {
(self.space.index + 1).min(self.ctx.spaces.len() - 1)
}
}
impl Space {
- fn new(index: usize, hard: bool, usable: Size) -> Space {
- Space {
+ fn new(index: usize, hard: bool, usable: Size) -> Self {
+ Self {
index,
hard,
layouts: vec![],
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 5c18cd32..b95110b7 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -1,17 +1,23 @@
-//! The text layouter layouts continous pieces of text into boxes.
+//! Layouting of continous pieces of text into boxes.
//!
//! The layouter picks the most suitable font for each individual character.
//! When the primary layouting axis horizontally inversed, the word is spelled
//! backwards. Vertical word layout is not yet supported.
-use ttf_parser::GlyphId;
use fontdock::{FaceId, FaceQuery, FontStyle};
+use ttf_parser::GlyphId;
+
use crate::font::SharedFontLoader;
use crate::geom::Size;
use crate::style::TextStyle;
use super::elements::{LayoutElement, Shaped};
use super::*;
+/// Layouts text into a box.
+pub async fn layout_text(text: &str, ctx: TextContext<'_>) -> BoxLayout {
+ TextLayouter::new(text, ctx).layout().await
+}
+
/// Performs the text layouting.
#[derive(Debug)]
struct TextLayouter<'a> {
@@ -26,28 +32,24 @@ struct TextLayouter<'a> {
/// The context for text layouting.
#[derive(Debug, Copy, Clone)]
pub struct TextContext<'a> {
- /// The font loader to retrieve fonts from when typesetting text
- /// using [`layout_text`].
+ /// The font loader to retrieve fonts from when typesetting text with
+ /// `layout_text`.
pub loader: &'a SharedFontLoader,
/// The style for text: Font selection with classes, weights and variants,
/// font sizes, spacing and so on.
pub style: &'a TextStyle,
- /// The axes along which the word is laid out. For now, only
- /// primary-horizontal layouting is supported.
- pub axes: LayoutAxes,
- /// The alignment of the finished layout.
+ /// The direction into which the word is laid out. For now, only horizontal
+ /// directions are supported.
+ pub dir: Dir,
+ /// The alignment of the _resulting_ layout. This does not effect the line
+ /// layouting itself, but rather how the finished layout will be positioned
+ /// in a parent layout.
pub align: LayoutAlign,
}
-/// Layouts text into a box.
-pub async fn layout_text(text: &str, ctx: TextContext<'_>) -> BoxLayout {
- TextLayouter::new(text, ctx).layout().await
-}
-
impl<'a> TextLayouter<'a> {
- /// Create a new text layouter.
- fn new(text: &'a str, ctx: TextContext<'a>) -> TextLayouter<'a> {
- TextLayouter {
+ fn new(text: &'a str, ctx: TextContext<'a>) -> Self {
+ Self {
ctx,
text,
shaped: Shaped::new(FaceId::MAX, ctx.style.font_size()),
@@ -57,10 +59,9 @@ impl<'a> TextLayouter<'a> {
}
}
- /// Do the layouting.
async fn layout(mut self) -> BoxLayout {
// If the primary axis is negative, we layout the characters reversed.
- if self.ctx.axes.primary.is_positive() {
+ if self.ctx.dir.is_positive() {
for c in self.text.chars() {
self.layout_char(c).await;
}
@@ -83,7 +84,6 @@ impl<'a> TextLayouter<'a> {
}
}
- /// Layout an individual character.
async fn layout_char(&mut self, c: char) {
let (index, glyph, char_width) = match self.select_font(c).await {
Some(selected) => selected,
@@ -115,11 +115,8 @@ impl<'a> TextLayouter<'a> {
self.width += char_width;
}
- /// Select the best font for a character and return its index along with
- /// the width of the char in the font.
async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> {
let mut loader = self.ctx.loader.borrow_mut();
-
let mut variant = self.ctx.style.variant;
if self.ctx.style.bolder {
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 44c59211..d20fc666 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -1,19 +1,26 @@
-//! The tree layouter layouts trees (i.e.
-//! [syntax trees](crate::syntax::SyntaxTree) and [functions](crate::func))
-//! by executing commands issued by the trees.
+//! Layouting of syntax trees.
-use crate::{Pass, Feedback, DynFuture};
use crate::style::LayoutStyle;
use crate::syntax::decoration::Decoration;
-use crate::syntax::tree::{SyntaxTree, SyntaxNode, DynamicNode};
use crate::syntax::span::{Span, Spanned};
-use super::line::{LineLayouter, LineContext};
+use crate::syntax::tree::{DynamicNode, SyntaxNode, SyntaxTree};
+use crate::{DynFuture, Feedback, Pass};
+use super::line::{LineContext, LineLayouter};
use super::text::{layout_text, TextContext};
use super::*;
+/// Layout a syntax tree into a collection of boxes.
+pub async fn layout_tree(
+ tree: &SyntaxTree,
+ ctx: LayoutContext<'_>,
+) -> Pass<MultiLayout> {
+ let mut layouter = TreeLayouter::new(ctx);
+ layouter.layout_tree(tree).await;
+ layouter.finish()
+}
+
/// Performs the tree layouting.
-#[derive(Debug)]
-pub struct TreeLayouter<'a> {
+struct TreeLayouter<'a> {
ctx: LayoutContext<'a>,
layouter: LineLayouter,
style: LayoutStyle,
@@ -21,9 +28,8 @@ pub struct TreeLayouter<'a> {
}
impl<'a> TreeLayouter<'a> {
- /// Create a new tree layouter.
- pub fn new(ctx: LayoutContext<'a>) -> TreeLayouter<'a> {
- TreeLayouter {
+ fn new(ctx: LayoutContext<'a>) -> Self {
+ Self {
layouter: LineLayouter::new(LineContext {
spaces: ctx.spaces.clone(),
axes: ctx.axes,
@@ -37,15 +43,17 @@ impl<'a> TreeLayouter<'a> {
}
}
- /// Layout a syntax tree by directly processing the nodes instead of using
- /// the command based architecture.
- pub async fn layout_tree(&mut self, tree: &SyntaxTree) {
+ async fn layout_tree(&mut self, tree: &SyntaxTree) {
for node in tree {
self.layout_node(node).await;
}
}
- pub async fn layout_node(&mut self, node: &Spanned<SyntaxNode>) {
+ fn finish(self) -> Pass<MultiLayout> {
+ Pass::new(self.layouter.finish(), self.feedback)
+ }
+
+ async fn layout_node(&mut self, node: &Spanned<SyntaxNode>) {
let decorate = |this: &mut TreeLayouter, deco| {
this.feedback.decorations.push(Spanned::new(deco, node.span));
};
@@ -80,7 +88,10 @@ impl<'a> TreeLayouter<'a> {
SyntaxNode::Raw(lines) => {
// TODO: Make this more efficient.
let fallback = self.style.text.fallback.clone();
- self.style.text.fallback.list_mut().insert(0, "monospace".to_string());
+ self.style.text.fallback
+ .list_mut()
+ .insert(0, "monospace".to_string());
+
self.style.text.fallback.flatten();
// Layout the first line.
@@ -104,17 +115,15 @@ impl<'a> TreeLayouter<'a> {
}
}
- /// Layout a node into this layouting process.
- pub async fn layout_dyn(&mut self, dynamic: Spanned<&dyn DynamicNode>) {
- // Execute the tree's layout function which generates the commands.
+ async fn layout_dyn(&mut self, dynamic: Spanned<&dyn DynamicNode>) {
+ // Execute the tree's command-generating layout function.
let layouted = dynamic.v.layout(LayoutContext {
style: &self.style,
spaces: self.layouter.remaining(),
- nested: true,
- .. self.ctx
+ root: true,
+ ..self.ctx
}).await;
- // Add the errors generated by the tree to the error list.
self.feedback.extend_offset(layouted.feedback, dynamic.span.start);
for command in layouted.output {
@@ -122,13 +131,6 @@ impl<'a> TreeLayouter<'a> {
}
}
- /// Compute the finished list of boxes.
- pub fn finish(self) -> Pass<MultiLayout> {
- Pass::new(self.layouter.finish(), self.feedback)
- }
-
- /// Execute a command issued by a tree. When the command is errorful, the
- /// given span is stored with the error.
fn execute_command<'r>(
&'r mut self,
command: Command<'r>,
@@ -149,13 +151,13 @@ impl<'a> TreeLayouter<'a> {
BreakLine => self.layouter.finish_line(),
BreakParagraph => self.layout_paragraph(),
BreakPage => {
- if self.ctx.nested {
+ if self.ctx.root {
+ self.layouter.finish_space(true)
+ } else {
error!(
@self.feedback, tree_span,
- "page break cannot be issued from nested context",
+ "page break cannot only be issued from root context",
);
- } else {
- self.layouter.finish_space(true)
}
}
@@ -164,12 +166,7 @@ impl<'a> TreeLayouter<'a> {
self.style.text = style;
}
SetPageStyle(style) => {
- if self.ctx.nested {
- error!(
- @self.feedback, tree_span,
- "page style cannot be changed from nested context",
- );
- } else {
+ if self.ctx.root {
self.style.page = style;
// The line layouter has no idea of page styles and thus we
@@ -184,6 +181,11 @@ impl<'a> TreeLayouter<'a> {
expansion: LayoutExpansion::new(true, true),
}
], true);
+ } else {
+ error!(
+ @self.feedback, tree_span,
+ "page style cannot only be changed from root context",
+ );
}
}
@@ -195,17 +197,20 @@ impl<'a> TreeLayouter<'a> {
}
}) }
- /// Layout a continous piece of text and add it to the line layouter.
async fn layout_text(&mut self, text: &str) {
- self.layouter.add(layout_text(text, TextContext {
- loader: &self.ctx.loader,
- style: &self.style.text,
- axes: self.ctx.axes,
- align: self.ctx.align,
- }).await)
+ self.layouter.add(
+ layout_text(
+ text,
+ TextContext {
+ loader: &self.ctx.loader,
+ style: &self.style.text,
+ dir: self.ctx.axes.primary,
+ align: self.ctx.align,
+ }
+ ).await
+ );
}
- /// Add the spacing for a syntactic space node.
fn layout_space(&mut self) {
self.layouter.add_primary_spacing(
self.style.text.word_spacing(),
@@ -213,7 +218,6 @@ impl<'a> TreeLayouter<'a> {
);
}
- /// Finish the paragraph and add paragraph spacing.
fn layout_paragraph(&mut self) {
self.layouter.add_secondary_spacing(
self.style.text.paragraph_spacing(),
diff --git a/src/length.rs b/src/length.rs
index 8e07a8e6..5634da19 100644
--- a/src/length.rs
+++ b/src/length.rs
@@ -84,7 +84,7 @@ impl Length {
}
/// Convert this to a length with a different unit.
- pub fn with_unit(self, unit: Unit) -> Length {
+ pub fn with_unit(self, unit: Unit) -> Self {
Self {
val: self.val * self.unit.raw_scale() / unit.raw_scale(),
unit,
@@ -160,7 +160,7 @@ impl FromStr for Length {
src[..split]
.parse::<f64>()
- .map(|val| Length::new(val, unit))
+ .map(|val| Self::new(val, unit))
.map_err(|_| ParseLengthError)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 5a0b8d0b..fa6300bb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,49 +2,54 @@
//!
//! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an
-//! [iterator of tokens](crate::syntax::Tokens). Then, a parser constructs a
-//! syntax tree from the token stream. The structures describing the tree can
-//! be found in the [syntax](crate::syntax) module.
+//! [iterator of tokens][tokens]. Then, a parser constructs a syntax tree from
+//! the token stream. The structures describing the tree can be found in the
+//! [syntax] module.
//! - **Layouting:** The next step is to transform the syntax tree into a
//! portable representation of the typesetted document. Types for these can be
-//! found in the [layout](crate::layout) module. A finished layout reading for
-//! exporting is a [MultiLayout](crate::layout::MultiLayout) consisting of
-//! multiple boxes (or pages).
+//! found in the [layout] module. A finished layout ready for exporting is a
+//! [`MultiLayout`] consisting of multiple boxes (or pages).
//! - **Exporting:** The finished layout can then be exported into a supported
-//! format. Submodules for these formats are located in the
-//! [export](crate::export) module. Currently, the only supported output
-//! format is [_PDF_](crate::export::pdf).
-
-use std::fmt::Debug;
-use std::future::Future;
-use std::pin::Pin;
-
-use crate::diagnostic::Diagnostics;
-use crate::font::SharedFontLoader;
-use crate::layout::MultiLayout;
-use crate::style::{LayoutStyle, PageStyle, TextStyle};
-use crate::syntax::decoration::Decorations;
-use crate::syntax::tree::SyntaxTree;
-use crate::syntax::parsing::{parse, ParseState};
-use crate::syntax::scope::Scope;
-use crate::syntax::span::{Offset, Pos};
+//! format. Submodules for these formats are located in the [export] module.
+//! Currently, the only supported output format is [_PDF_].
+//!
+//! [tokens]: syntax/tokens/struct.Tokens.html
+//! [syntax]: syntax/index.html
+//! [layout]: layout/index.html
+//! [export]: export/index.html
+//! [_PDF_]: export/pdf/index.html
+//! [`MultiLayout`]: layout/type.MultiLayout.html
#[macro_use]
mod macros;
#[macro_use]
pub mod diagnostic;
-pub mod export;
-pub mod font;
#[macro_use]
pub mod func;
+
+pub mod export;
+pub mod font;
pub mod geom;
pub mod layout;
-pub mod library;
pub mod length;
+pub mod library;
pub mod paper;
pub mod style;
pub mod syntax;
+use std::fmt::Debug;
+use std::future::Future;
+use std::pin::Pin;
+
+use crate::diagnostic::Diagnostics;
+use crate::font::SharedFontLoader;
+use crate::layout::MultiLayout;
+use crate::style::{LayoutStyle, PageStyle, TextStyle};
+use crate::syntax::decoration::Decorations;
+use crate::syntax::parsing::{parse, ParseState};
+use crate::syntax::span::{Offset, Pos};
+use crate::syntax::tree::SyntaxTree;
+
/// Transforms source code into typesetted layouts.
///
/// A typesetter can be configured through various methods.
@@ -59,11 +64,11 @@ pub struct Typesetter {
impl Typesetter {
/// Create a new typesetter.
- pub fn new(loader: SharedFontLoader) -> Typesetter {
- Typesetter {
+ pub fn new(loader: SharedFontLoader) -> Self {
+ Self {
loader,
style: LayoutStyle::default(),
- parse_state: ParseState { scope: Scope::with_std() },
+ parse_state: ParseState { scope: crate::library::std() },
}
}
@@ -100,9 +105,9 @@ impl Typesetter {
expansion: LayoutExpansion::new(true, true),
}],
repeat: true,
- axes: LayoutAxes::new(LTT, TTB),
+ axes: LayoutAxes::new(LTR, TTB),
align: LayoutAlign::new(Start, Start),
- nested: false,
+ root: true,
},
).await
}
@@ -119,7 +124,7 @@ impl Typesetter {
/// A dynamic future type which allows recursive invocation of async functions
/// when used as the return type. This is also how the async trait functions
/// work internally.
-pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>;
+pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
/// The result of some pass: Some output `T` and feedback data.
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -132,12 +137,12 @@ pub struct Pass<T> {
impl<T> Pass<T> {
/// Create a new pass from output and feedback data.
- pub fn new(output: T, feedback: Feedback) -> Pass<T> {
- Pass { output, feedback }
+ pub fn new(output: T, feedback: Feedback) -> Self {
+ Self { output, feedback }
}
/// Map the output type and keep the feedback data.
- pub fn map<F, U>(self, f: F) -> Pass<U> where F: FnOnce(T) -> U {
+ pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Pass<U> {
Pass {
output: f(self.output),
feedback: self.feedback,
@@ -145,10 +150,10 @@ impl<T> Pass<T> {
}
}
-/// User feedback data accumulated during a compilation pass.
+/// Diagnostic and semantic syntax highlighting data.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Feedback {
- /// Diagnostics in the source code.
+ /// Diagnostics about the source code.
pub diagnostics: Diagnostics,
/// Decorations of the source code for semantic syntax highlighting.
pub decorations: Decorations,
@@ -156,28 +161,28 @@ pub struct Feedback {
impl Feedback {
/// Create a new feedback instance without errors and decos.
- pub fn new() -> Feedback {
- Feedback {
+ pub fn new() -> Self {
+ Self {
diagnostics: vec![],
decorations: vec![],
}
}
/// Merged two feedbacks into one.
- pub fn merge(mut a: Feedback, b: Feedback) -> Feedback {
+ pub fn merge(mut a: Self, b: Self) -> Self {
a.extend(b);
a
}
/// Add other feedback data to this feedback.
- pub fn extend(&mut self, other: Feedback) {
- self.diagnostics.extend(other.diagnostics);
- self.decorations.extend(other.decorations);
+ pub fn extend(&mut self, more: Self) {
+ self.diagnostics.extend(more.diagnostics);
+ self.decorations.extend(more.decorations);
}
/// Add more feedback whose spans are local and need to be offset by an
/// `offset` to be correct in this feedback's context.
- pub fn extend_offset(&mut self, more: Feedback, offset: Pos) {
+ pub fn extend_offset(&mut self, more: Self, offset: Pos) {
self.diagnostics.extend(more.diagnostics.offset(offset));
self.decorations.extend(more.decorations.offset(offset));
}
diff --git a/src/library/font.rs b/src/library/font.rs
index 21ac14c7..6e711021 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -1,6 +1,5 @@
-//! Font configuration.
-
use fontdock::{FontStyle, FontWeight, FontWidth};
+
use crate::length::ScaleLength;
use super::*;
@@ -19,7 +18,6 @@ function! {
parse(header, body, state, f) {
let size = header.args.pos.get::<ScaleLength>();
-
let style = header.args.key.get::<FontStyle>("style", f);
let weight = header.args.key.get::<FontWeight>("weight", f);
let width = header.args.key.get::<FontWidth>("width", f);
@@ -40,7 +38,7 @@ function! {
})
.collect();
- FontFunc {
+ Self {
body: parse_maybe_body(body, state, f),
size,
style,
@@ -52,30 +50,39 @@ function! {
}
layout(self, ctx, f) {
- styled(&self.body, ctx, Some(()), |t, _| {
- self.size.with(|s| match s {
- ScaleLength::Absolute(length) => {
- t.base_font_size = length.as_raw();
- t.font_scale = 1.0;
- }
- ScaleLength::Scaled(scale) => t.font_scale = scale,
- });
+ let mut text = ctx.style.text.clone();
- self.style.with(|s| t.variant.style = s);
- self.weight.with(|w| t.variant.weight = w);
- self.width.with(|w| t.variant.width = w);
-
- if !self.list.is_empty() {
- *t.fallback.list_mut() = self.list.iter()
- .map(|s| s.to_lowercase())
- .collect();
+ self.size.with(|s| match s {
+ ScaleLength::Absolute(length) => {
+ text.base_font_size = length.as_raw();
+ text.font_scale = 1.0;
}
+ ScaleLength::Scaled(scale) => text.font_scale = scale,
+ });
- for (class, fallback) in &self.classes {
- t.fallback.set_class_list(class.clone(), fallback.clone());
- }
+ self.style.with(|s| text.variant.style = s);
+ self.weight.with(|w| text.variant.weight = w);
+ self.width.with(|w| text.variant.width = w);
- t.fallback.flatten();
- })
+ if !self.list.is_empty() {
+ *text.fallback.list_mut() = self.list.iter()
+ .map(|s| s.to_lowercase())
+ .collect();
+ }
+
+ for (class, fallback) in &self.classes {
+ text.fallback.set_class_list(class.clone(), fallback.clone());
+ }
+
+ text.fallback.flatten();
+
+ match &self.body {
+ Some(tree) => vec![
+ SetTextStyle(text),
+ LayoutSyntaxTree(tree),
+ SetTextStyle(ctx.style.text.clone()),
+ ],
+ None => vec![SetTextStyle(text)],
+ }
}
}
diff --git a/src/library/layout.rs b/src/library/layout.rs
index d6d02436..f3ddaadf 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -1,5 +1,3 @@
-//! Layout building blocks.
-
use crate::length::ScaleLength;
use super::*;
@@ -13,7 +11,7 @@ function! {
}
parse(header, body, state, f) {
- BoxFunc {
+ Self {
body: parse_maybe_body(body, state, f).unwrap_or(SyntaxTree::new()),
width: header.args.key.get::<ScaleLength>("width", f),
height: header.args.key.get::<ScaleLength>("height", f),
@@ -21,8 +19,8 @@ function! {
}
layout(self, ctx, f) {
- ctx.repeat = false;
ctx.spaces.truncate(1);
+ ctx.repeat = false;
self.width.with(|v| {
let length = v.raw_scaled(ctx.base.x);
@@ -51,13 +49,13 @@ function! {
#[derive(Debug, Clone, PartialEq)]
pub struct AlignFunc {
body: Option<SyntaxTree>,
- aligns: Vec<Spanned<SpecAlign>>,
+ aligns: SpanVec<SpecAlign>,
h: Option<Spanned<SpecAlign>>,
v: Option<Spanned<SpecAlign>>,
}
parse(header, body, state, f) {
- AlignFunc {
+ Self {
body: parse_maybe_body(body, state, f),
aligns: header.args.pos.all::<Spanned<SpecAlign>>().collect(),
h: header.args.key.get::<Spanned<SpecAlign>>("horizontal", f),
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 7240e42b..0f61b901 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,16 +1,19 @@
//! The standard library.
+mod font;
+mod layout;
+mod page;
+mod spacing;
+
+pub use font::*;
+pub use layout::*;
+pub use page::*;
+pub use spacing::*;
+
use crate::func::prelude::*;
-use crate::layout::{LayoutContext, Commands};
use crate::syntax::scope::Scope;
-macro_rules! lib { ($name:ident) => { mod $name; pub use $name::*; }}
-lib!(font);
-lib!(layout);
-lib!(page);
-lib!(spacing);
-
-/// Create a scope with all standard functions.
+/// Create a scope with all standard library functions.
pub fn std() -> Scope {
let mut std = Scope::new::<ValFunc>();
@@ -28,7 +31,10 @@ pub fn std() -> Scope {
}
function! {
- /// `val`: Layouts the body with no special effect.
+ /// `val`: Ignores all arguments and layouts the body flatly.
+ ///
+ /// This is also the fallback function, which is used when a function name
+ /// could not be resolved.
#[derive(Debug, Clone, PartialEq)]
pub struct ValFunc {
body: Option<SyntaxTree>,
@@ -37,7 +43,7 @@ function! {
parse(header, body, state, f) {
header.args.pos.0.clear();
header.args.key.0.clear();
- ValFunc { body: parse_maybe_body(body, state, f), }
+ Self { body: parse_maybe_body(body, state, f), }
}
layout(self, ctx, f) {
@@ -47,27 +53,3 @@ function! {
}
}
}
-
-/// Layout an optional body with a change of the text style.
-fn styled<'a, T, F>(
- body: &'a Option<SyntaxTree>,
- ctx: LayoutContext<'_>,
- data: Option<T>,
- f: F,
-) -> Commands<'a> where F: FnOnce(&mut TextStyle, T) {
- if let Some(data) = data {
- let mut style = ctx.style.text.clone();
- f(&mut style, data);
-
- match body {
- Some(tree) => vec![
- SetTextStyle(style),
- LayoutSyntaxTree(tree),
- SetTextStyle(ctx.style.text.clone()),
- ],
- None => vec![SetTextStyle(style)],
- }
- } else {
- vec![]
- }
-}
diff --git a/src/library/page.rs b/src/library/page.rs
index faf08ee0..1cc76d7f 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -1,5 +1,3 @@
-//! Page setup.
-
use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass};
use super::*;
@@ -21,7 +19,7 @@ function! {
parse(header, body, state, f) {
expect_no_body(body, f);
- PageFunc {
+ Self {
paper: header.args.pos.get::<Paper>(),
width: header.args.key.get::<Length>("width", f),
height: header.args.key.get::<Length>("height", f),
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index d8263694..545f3910 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -1,13 +1,11 @@
-//! Spacing.
-
-use crate::length::ScaleLength;
use crate::layout::SpacingKind;
+use crate::length::ScaleLength;
use super::*;
function! {
/// `parbreak`: Ends the current paragraph.
///
- /// self has the same effect as two subsequent newlines.
+ /// This has the same effect as two subsequent newlines.
#[derive(Debug, Default, Clone, PartialEq)]
pub struct ParBreakFunc;
@@ -25,7 +23,7 @@ function! {
}
function! {
- /// `h` and `v`: Add spacing along an axis.
+ /// `h` and `v`: Add horizontal or vertical spacing.
#[derive(Debug, Clone, PartialEq)]
pub struct SpacingFunc {
spacing: Option<(SpecAxis, ScaleLength)>,
@@ -35,7 +33,7 @@ function! {
parse(header, body, state, f, meta) {
expect_no_body(body, f);
- SpacingFunc {
+ Self {
spacing: header.args.pos.expect::<ScaleLength>(f)
.map(|s| (meta, s))
.or_missing(header.name.span, "spacing", f),
diff --git a/src/macros.rs b/src/macros.rs
index 8ba32650..2236c480 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -9,7 +9,7 @@ macro_rules! try_or {
($result:expr, $or:expr $(,)?) => {
match $result {
Ok(v) => v,
- Err(_) => { $or }
+ Err(_) => $or,
}
};
}
@@ -19,7 +19,7 @@ macro_rules! try_opt_or {
($option:expr, $or:expr $(,)?) => {
match $option {
Some(v) => v,
- None => { $or }
+ None => $or,
}
};
}
diff --git a/src/paper.rs b/src/paper.rs
index e8913d12..97f76703 100644
--- a/src/paper.rs
+++ b/src/paper.rs
@@ -16,7 +16,7 @@ pub struct Paper {
impl Paper {
/// The paper with the given name.
- pub fn from_name(name: &str) -> Option<Paper> {
+ pub fn from_name(name: &str) -> Option<Self> {
parse_paper(name)
}
@@ -39,7 +39,6 @@ pub enum PaperClass {
impl PaperClass {
/// The default margins for this page class.
pub fn default_margins(self) -> Value4<ScaleLength> {
- use PaperClass::*;
let values = |l, t, r, b| Value4::new(
ScaleLength::Scaled(l),
ScaleLength::Scaled(t),
@@ -48,11 +47,11 @@ impl PaperClass {
);
match self {
- Custom => values(0.1190, 0.0842, 0.1190, 0.0842),
- Base => values(0.1190, 0.0842, 0.1190, 0.0842),
- US => values(0.1760, 0.1092, 0.1760, 0.0910),
- Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294),
- Book => values(0.1200, 0.0852, 0.1500, 0.0965),
+ Self::Custom => values(0.1190, 0.0842, 0.1190, 0.0842),
+ Self::Base => values(0.1190, 0.0842, 0.1190, 0.0842),
+ Self::US => values(0.1760, 0.1092, 0.1760, 0.0910),
+ Self::Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294),
+ Self::Book => values(0.1200, 0.0852, 0.1500, 0.0965),
}
}
}
diff --git a/src/style.rs b/src/style.rs
index 9dce381e..80389919 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,7 +1,10 @@
//! Styles for text and pages.
-use fontdock::{fallback, FallbackTree, FontVariant, FontStyle, FontWeight, FontWidth};
-use crate::geom::{Size, Margins, Value4};
+use fontdock::{
+ fallback, FallbackTree, FontStyle, FontVariant, FontWeight, FontWidth,
+};
+
+use crate::geom::{Margins, Size, Value4};
use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass, PAPER_A4};
@@ -62,8 +65,8 @@ impl TextStyle {
}
impl Default for TextStyle {
- fn default() -> TextStyle {
- TextStyle {
+ fn default() -> Self {
+ Self {
fallback: fallback! {
list: ["sans-serif"],
classes: {
@@ -105,8 +108,8 @@ pub struct PageStyle {
impl PageStyle {
/// The default page style for the given paper.
- pub fn new(paper: Paper) -> PageStyle {
- PageStyle {
+ pub fn new(paper: Paper) -> Self {
+ Self {
class: paper.class,
size: paper.size(),
margins: Value4::with_all(None),
@@ -127,7 +130,7 @@ impl PageStyle {
}
impl Default for PageStyle {
- fn default() -> PageStyle {
- PageStyle::new(PAPER_A4)
+ fn default() -> Self {
+ Self::new(PAPER_A4)
}
}
diff --git a/src/syntax/decoration.rs b/src/syntax/decoration.rs
index 13a9ad36..a9097444 100644
--- a/src/syntax/decoration.rs
+++ b/src/syntax/decoration.rs
@@ -13,32 +13,16 @@ pub type Decorations = SpanVec<Decoration>;
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
pub enum Decoration {
- /// A valid function name.
- /// ```typst
- /// [box]
- /// ^^^
- /// ```
- ValidFuncName,
- /// An invalid function name.
- /// ```typst
- /// [blabla]
- /// ^^^^^^
- /// ```
- InvalidFuncName,
- /// A key of a keyword argument.
- /// ```typst
- /// [box: width=5cm]
- /// ^^^^^
- /// ```
+ /// A valid, successfully resolved function name.
+ ResolvedFunc,
+ /// An invalid, unresolved function name.
+ UnresolvedFunc,
+ /// A key part of a keyword argument.
ArgumentKey,
- /// A key in an object.
- /// ```typst
- /// [box: padding={ left: 1cm, right: 2cm}]
- /// ^^^^ ^^^^^
- /// ```
+ /// A key part of a pair in an object.
ObjectKey,
- /// An italic word.
+ /// Text in italics.
Italic,
- /// A bold word.
+ /// Text in bold.
Bold,
}
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index 0a9ab149..a637f115 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -5,26 +5,26 @@ use std::ops::Deref;
use std::str::FromStr;
use std::u8;
-use crate::Feedback;
use crate::length::Length;
-use super::span::{Spanned, SpanVec};
+use crate::Feedback;
+use super::span::{SpanVec, Spanned};
use super::tokens::is_identifier;
use super::value::Value;
-/// An argument or return value.
+/// An expression.
#[derive(Clone, PartialEq)]
pub enum Expr {
/// An identifier: `ident`.
Ident(Ident),
/// A string: `"string"`.
Str(String),
+ /// A boolean: `true, false`.
+ Bool(bool),
/// A number: `1.2, 200%`.
Number(f64),
/// A length: `2cm, 5.2in`.
Length(Length),
- /// A bool: `true, false`.
- Bool(bool),
- /// A color value, including the alpha channel: `#f79143ff`.
+ /// A color value with alpha channel: `#f79143ff`.
Color(RgbaColor),
/// A tuple: `(false, 12cm, "hi")`.
Tuple(Tuple),
@@ -32,37 +32,38 @@ pub enum Expr {
NamedTuple(NamedTuple),
/// An object: `{ fit: false, width: 12pt }`.
Object(Object),
- /// An operator that negates the contained expression.
+ /// An operation that negates the contained expression.
Neg(Box<Spanned<Expr>>),
- /// An operator that adds the contained expressions.
+ /// An operation that adds the contained expressions.
Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operator that subtracts contained expressions.
+ /// An operation that subtracts the contained expressions.
Sub(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operator that multiplies the contained expressions.
+ /// An operation that multiplies the contained expressions.
Mul(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operator that divides the contained expressions.
+ /// An operation that divides the contained expressions.
Div(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
}
impl Expr {
- /// A natural-language name of the type of this expression, e.g. "identifier".
+ /// A natural-language name of the type of this expression, e.g.
+ /// "identifier".
pub fn name(&self) -> &'static str {
use Expr::*;
match self {
- Ident(_) => "identifier",
- Str(_) => "string",
- Number(_) => "number",
- Length(_) => "length",
- Bool(_) => "bool",
- Color(_) => "color",
- Tuple(_) => "tuple",
+ Ident(_) => "identifier",
+ Str(_) => "string",
+ Bool(_) => "bool",
+ Number(_) => "number",
+ Length(_) => "length",
+ Color(_) => "color",
+ Tuple(_) => "tuple",
NamedTuple(_) => "named tuple",
- Object(_) => "object",
- Neg(_) => "negation",
- Add(_, _) => "addition",
- Sub(_, _) => "subtraction",
- Mul(_, _) => "multiplication",
- Div(_, _) => "division",
+ Object(_) => "object",
+ Neg(_) => "negation",
+ Add(_, _) => "addition",
+ Sub(_, _) => "subtraction",
+ Mul(_, _) => "multiplication",
+ Div(_, _) => "division",
}
}
}
@@ -73,9 +74,9 @@ impl Debug for Expr {
match self {
Ident(i) => i.fmt(f),
Str(s) => s.fmt(f),
+ Bool(b) => b.fmt(f),
Number(n) => n.fmt(f),
Length(s) => s.fmt(f),
- Bool(b) => b.fmt(f),
Color(c) => c.fmt(f),
Tuple(t) => t.fmt(f),
NamedTuple(t) => t.fmt(f),
@@ -89,22 +90,15 @@ impl Debug for Expr {
}
}
-/// A unicode identifier.
-///
-/// # Example
-/// ```typst
-/// [func: "hi", ident]
-/// ^^^^ ^^^^^
-/// ```
+/// An identifier as defined by unicode with a few extra permissible characters.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ident(pub String);
impl Ident {
- /// Create a new identifier from a string checking that it is a valid
- /// unicode identifier.
- pub fn new<S>(ident: S) -> Option<Ident> where S: AsRef<str> + Into<String> {
+ /// Create a new identifier from a string checking that it is a valid.
+ pub fn new(ident: impl AsRef<str> + Into<String>) -> Option<Self> {
if is_identifier(ident.as_ref()) {
- Some(Ident(ident.into()))
+ Some(Self(ident.into()))
} else {
None
}
@@ -139,36 +133,36 @@ pub struct RgbaColor {
pub b: u8,
/// Alpha channel.
pub a: u8,
- /// Indicates whether this is a user-provided value or a
- /// default value provided as a fail-over by the parser.
- /// This color may be overwritten if this property is true.
+ /// This is true if this value was provided as a fail-over by the parser
+ /// because the user-defined value was invalid. This color may be
+ /// overwritten if this property is true.
pub healed: bool,
}
impl RgbaColor {
/// Constructs a new color.
- pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor {
- RgbaColor { r, g, b, a, healed: false }
+ pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
+ Self { r, g, b, a, healed: false }
}
/// Constructs a new color with the healed property set to true.
- pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> RgbaColor {
- RgbaColor { r, g, b, a, healed: true }
+ pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> Self {
+ Self { r, g, b, a, healed: true }
}
}
impl FromStr for RgbaColor {
type Err = ParseColorError;
- /// Constructs a new color from a hex string like `7a03c2`.
- /// Do not specify a leading `#`.
- fn from_str(hex_str: &str) -> Result<RgbaColor, Self::Err> {
+ /// Constructs a new color from a hex string like `7a03c2`. Do not specify a
+ /// leading `#`.
+ fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
if !hex_str.is_ascii() {
return Err(ParseColorError);
}
let len = hex_str.len();
- let long = len == 6 || len == 8;
+ let long = len == 6 || len == 8;
let short = len == 3 || len == 4;
let alpha = len == 4 || len == 8;
@@ -192,7 +186,7 @@ impl FromStr for RgbaColor {
}
}
- Ok(RgbaColor::new(values[0], values[1], values[2], values[3]))
+ Ok(Self::new(values[0], values[1], values[2], values[3]))
}
}
@@ -200,14 +194,12 @@ impl Debug for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() {
write!(
- f,
- "rgba({:02}, {:02}, {:02}, {:02})",
+ f, "rgba({:02}, {:02}, {:02}, {:02})",
self.r, self.g, self.b, self.a,
)?;
} else {
write!(
- f,
- "#{:02x}{:02x}{:02x}{:02x}",
+ f, "#{:02x}{:02x}{:02x}{:02x}",
self.r, self.g, self.b, self.a,
)?;
}
@@ -218,7 +210,7 @@ impl Debug for RgbaColor {
}
}
-/// The error returned when parsing a [`RgbaColor`] from a string fails.
+/// The error when parsing an `RgbaColor` fails.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ParseColorError;
@@ -226,7 +218,7 @@ impl std::error::Error for ParseColorError {}
impl fmt::Display for ParseColorError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("invalid color")
+ f.pad("invalid color")
}
}
@@ -241,8 +233,8 @@ pub struct Tuple(pub SpanVec<Expr>);
impl Tuple {
/// Create an empty tuple.
- pub fn new() -> Tuple {
- Tuple(vec![])
+ pub fn new() -> Self {
+ Self(vec![])
}
/// Add an element.
@@ -278,13 +270,13 @@ impl Tuple {
let mut i = 0;
std::iter::from_fn(move || {
while i < self.0.len() {
- let val = V::parse(self.0[i].clone(), &mut Feedback::new());
- if val.is_some() {
- self.0.remove(i);
- return val;
- } else {
- i += 1;
- }
+ let val = V::parse(self.0[i].clone(), &mut Feedback::new());
+ if val.is_some() {
+ self.0.remove(i);
+ return val;
+ } else {
+ i += 1;
+ }
}
None
})
@@ -305,16 +297,16 @@ impl Debug for Tuple {
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct NamedTuple {
- /// The name of the tuple and where it is in the user source.
+ /// The name of the tuple.
pub name: Spanned<Ident>,
/// The elements of the tuple.
pub tuple: Spanned<Tuple>,
}
impl NamedTuple {
- /// Create a named tuple from a tuple.
- pub fn new(name: Spanned<Ident>, tuple: Spanned<Tuple>) -> NamedTuple {
- NamedTuple { name, tuple }
+ /// Create a named tuple from a name and a tuple.
+ pub fn new(name: Spanned<Ident>, tuple: Spanned<Tuple>) -> Self {
+ Self { name, tuple }
}
}
@@ -338,24 +330,14 @@ pub struct Object(pub SpanVec<Pair>);
/// A key-value pair in an object.
#[derive(Debug, Clone, PartialEq)]
pub struct Pair {
- /// The key part.
- /// ```typst
- /// key: value
- /// ^^^
- /// ```
pub key: Spanned<Ident>,
- /// The value part.
- /// ```typst
- /// key: value
- /// ^^^^^
- /// ```
pub value: Spanned<Expr>,
}
impl Object {
/// Create an empty object.
- pub fn new() -> Object {
- Object(vec![])
+ pub fn new() -> Self {
+ Self(vec![])
}
/// Add a pair to object.
@@ -384,13 +366,13 @@ impl Object {
let mut i = 0;
std::iter::from_fn(move || {
while i < self.0.len() {
- let val = V::parse(self.0[i].v.value.clone(), &mut Feedback::new());
- if let Some(val) = val {
- let pair = self.0.remove(i);
- return Some((pair.v.key, val));
- } else {
- i += 1;
- }
+ let val = V::parse(self.0[i].v.value.clone(), &mut Feedback::new());
+ if let Some(val) = val {
+ let pair = self.0.remove(i);
+ return Some((pair.v.key, val));
+ } else {
+ i += 1;
+ }
}
None
})
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 6b1f3d08..e1c9bbb0 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -4,19 +4,19 @@
#[macro_use]
mod test;
-/// Basic types used around the syntax side.
-pub mod prelude {
- pub use super::expr::*;
- pub use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
- pub use super::span::{SpanVec, Span, Spanned};
- pub use super::value::*;
-}
-
pub mod decoration;
pub mod expr;
-pub mod tree;
pub mod parsing;
-pub mod span;
pub mod scope;
+pub mod span;
pub mod tokens;
+pub mod tree;
pub mod value;
+
+/// Basic types used around the syntax side.
+pub mod prelude {
+ pub use super::expr::*;
+ pub use super::span::{Span, SpanVec, Spanned};
+ pub use super::tree::{DynamicNode, SyntaxNode, SyntaxTree};
+ pub use super::value::*;
+}
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index bcbcb8d4..5141e455 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -2,36 +2,34 @@
use std::str::FromStr;
-use crate::{Pass, Feedback};
+use crate::{Feedback, Pass};
use super::decoration::Decoration;
use super::expr::*;
use super::scope::Scope;
use super::span::{Pos, Span, Spanned};
-use super::tokens::{is_newline_char, Token, Tokens, TokenMode};
-use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
+use super::tokens::{is_newline_char, Token, TokenMode, Tokens};
+use super::tree::{DynamicNode, SyntaxNode, SyntaxTree};
-/// A function which parses a function call into a tree.
+/// A function which parses a function call into a dynamic node.
pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn DynamicNode>>;
/// Parse a function call.
pub trait ParseCall {
- /// A metadata type whose value is passed into the function parser. This
- /// allows a single function to do different things depending on the value
- /// that needs to be given when inserting the function into a
- /// [scope](crate::syntax::Scope).
+ /// Metadata whose value is passed to `parse`. This allows a single function
+ /// to do different things depending on the value that needs to be given
+ /// when inserting the function into a scope.
///
- /// For example, the functions `word.spacing`, `line.spacing` and
- /// `par.spacing` are actually all the same function
- /// [`ContentSpacingFunc`](crate::library::ContentSpacingFunc) with the
- /// metadata specifiy which content should be spaced.
+ /// For example, the functions `h` and `v` are built on the same type.
type Meta: Clone;
- /// Parse the header and body into this function given a context.
+ /// Parse the function call.
fn parse(
- header: FuncCall,
+ call: FuncCall,
state: &ParseState,
metadata: Self::Meta,
- ) -> Pass<Self> where Self: Sized;
+ ) -> Pass<Self>
+ where
+ Self: Sized;
}
/// An invocation of a function.
@@ -58,8 +56,8 @@ pub struct FuncArgs {
impl FuncArgs {
/// Create new empty function arguments.
- pub fn new() -> FuncArgs {
- FuncArgs {
+ pub fn new() -> Self {
+ Self {
pos: Tuple::new(),
key: Object::new(),
}
@@ -77,9 +75,7 @@ impl FuncArgs {
/// Either a positional or keyword argument.
#[derive(Debug, Clone, PartialEq)]
pub enum FuncArg {
- /// A positional argument.
Pos(Expr),
- /// A keyword argument.
Key(Pair),
}
@@ -116,26 +112,21 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxTree> {
Token::Function { header, body, terminated } => {
let parsed = FuncParser::new(header, body, state).parse();
feedback.extend_offset(parsed.feedback, span.start);
-
if !terminated {
error!(@feedback, Span::at(span.end), "expected closing bracket");
}
-
parsed.output
}
Token::Star => SyntaxNode::ToggleBolder,
Token::Underscore => SyntaxNode::ToggleItalic,
Token::Backslash => SyntaxNode::Linebreak,
-
Token::Raw { raw, terminated } => {
if !terminated {
error!(@feedback, Span::at(span.end), "expected backtick");
}
-
SyntaxNode::Raw(unescape_raw(raw))
}
-
Token::Text(text) => SyntaxNode::Text(text.to_string()),
Token::LineComment(_) | Token::BlockComment(_) => continue,
@@ -153,17 +144,9 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxTree> {
struct FuncParser<'s> {
state: &'s ParseState,
- /// ```typst
- /// [tokens][body]
- /// ^^^^^^
- /// ```
+ /// The tokens inside the header.
tokens: Tokens<'s>,
peeked: Option<Option<Spanned<Token<'s>>>>,
- /// The spanned body string if there is a body.
- /// ```typst
- /// [tokens][body]
- /// ^^^^
- /// ```
body: Option<Spanned<&'s str>>,
feedback: Feedback,
}
@@ -173,8 +156,8 @@ impl<'s> FuncParser<'s> {
header: &'s str,
body: Option<Spanned<&'s str>>,
state: &'s ParseState,
- ) -> FuncParser<'s> {
- FuncParser {
+ ) -> Self {
+ Self {
state,
// Start at column 1 because the opening bracket is also part of
// the function, but not part of the `header` string.
@@ -190,7 +173,7 @@ impl<'s> FuncParser<'s> {
let name = header.name.v.as_str();
let (parser, deco) = match self.state.scope.get_parser(name) {
// The function exists in the scope.
- Some(parser) => (parser, Decoration::ValidFuncName),
+ Some(parser) => (parser, Decoration::ResolvedFunc),
// The function does not exist in the scope. The parser that is
// returned here is a fallback parser which exists to make sure
@@ -199,7 +182,7 @@ impl<'s> FuncParser<'s> {
None => {
error!(@self.feedback, header.name.span, "unknown function");
let parser = self.state.scope.get_fallback_parser();
- (parser, Decoration::InvalidFuncName)
+ (parser, Decoration::UnresolvedFunc)
}
};
@@ -261,9 +244,8 @@ impl<'s> FuncParser<'s> {
self.skip_white();
let key = ident;
- self.feedback.decorations.push(
- Spanned::new(Decoration::ArgumentKey, key.span)
- );
+ self.feedback.decorations
+ .push(Spanned::new(Decoration::ArgumentKey, key.span));
let value = try_opt_or!(self.parse_expr(), {
self.expected("value");
@@ -399,9 +381,9 @@ impl FuncParser<'_> {
self.eat_span(Expr::Str(unescape_string(string)))
}
+ Token::ExprBool(b) => self.eat_span(Expr::Bool(b)),
Token::ExprNumber(n) => self.eat_span(Expr::Number(n)),
Token::ExprLength(s) => self.eat_span(Expr::Length(s)),
- Token::ExprBool(b) => self.eat_span(Expr::Bool(b)),
Token::ExprHex(s) => {
if let Ok(color) = RgbaColor::from_str(s) {
self.eat_span(Expr::Color(color))
@@ -411,7 +393,7 @@ impl FuncParser<'_> {
let healed = RgbaColor::new_healed(0, 0, 0, 255);
self.eat_span(Expr::Color(healed))
}
- },
+ }
// This could be a tuple or a parenthesized expression. We parse as
// a tuple in any case and coerce the tuple into a value if it is
@@ -500,9 +482,8 @@ impl FuncParser<'_> {
continue;
}
- self.feedback.decorations.push(
- Spanned::new(Decoration::ObjectKey, key.span)
- );
+ self.feedback.decorations
+ .push(Spanned::new(Decoration::ObjectKey, key.span));
self.skip_white();
let value = try_opt_or!(self.parse_expr(), {
@@ -622,7 +603,8 @@ impl<'s> FuncParser<'s> {
}
fn pos(&self) -> Pos {
- self.peeked.flatten()
+ self.peeked
+ .flatten()
.map(|s| s.span.start)
.unwrap_or_else(|| self.tokens.pos())
}
@@ -687,10 +669,10 @@ mod tests {
use super::*;
use Decoration::*;
- use Expr::{Number as Num, Length as Len, Bool};
+ use Expr::{Bool, Length as Len, Number as Num};
use SyntaxNode::{
- Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
- Parbreak, Linebreak,
+ Space as S, Parbreak, Linebreak, ToggleItalic as Italic,
+ ToggleBolder as Bold,
};
/// Test whether the given string parses into
@@ -882,7 +864,7 @@ mod tests {
p!("🌎\n*/[n]" =>
[(0:0, 0:1, T("🌎")), (0:1, 1:0, S), (1:2, 1:5, func!((0:1, 0:2, "n")))],
[(1:0, 1:2, "unexpected end of block comment")],
- [(1:3, 1:4, ValidFuncName)],
+ [(1:3, 1:4, ResolvedFunc)],
);
}
@@ -905,12 +887,12 @@ mod tests {
p!("[hi]" =>
[func!("hi")],
[(0:1, 0:3, "unknown function")],
- [(0:1, 0:3, InvalidFuncName)],
+ [(0:1, 0:3, UnresolvedFunc)],
);
// A valid name.
- p!("[f]" => [func!("f")], [], [(0:1, 0:2, ValidFuncName)]);
- p!("[ f]" => [func!("f")], [], [(0:3, 0:4, ValidFuncName)]);
+ p!("[f]" => [func!("f")], [], [(0:1, 0:2, ResolvedFunc)]);
+ p!("[ f]" => [func!("f")], [], [(0:3, 0:4, ResolvedFunc)]);
// An invalid token for a name.
p!("[12]" => [func!("")], [(0:1, 0:3, "expected function name, found number")], []);
@@ -923,7 +905,7 @@ mod tests {
// Valid.
p!("[val: true]" =>
[func!["val": (Bool(true))]], [],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
// No colon before arg.
@@ -936,7 +918,7 @@ mod tests {
p!("[val/🌎:$]" =>
[func!("val")],
[(0:4, 0:4, "expected colon")],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
// String in invalid header without colon still parsed as string
@@ -1159,20 +1141,20 @@ mod tests {
// Correct
p!("[val: x=true]" =>
[func!("val": (), { "x" => Bool(true) })], [],
- [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ValidFuncName)],
+ [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ResolvedFunc)],
);
// Spacing around keyword arguments
p!("\n [val: \n hi \n = /* //\n */ \"s\n\"]" =>
[S, func!("val": (), { "hi" => Str("s\n") })], [],
- [(2:1, 2:3, ArgumentKey), (1:2, 1:5, ValidFuncName)],
+ [(2:1, 2:3, ArgumentKey), (1:2, 1:5, ResolvedFunc)],
);
// Missing value
p!("[val: x=]" =>
[func!("val")],
[(0:8, 0:8, "expected value")],
- [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ValidFuncName)],
+ [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ResolvedFunc)],
);
}
@@ -1180,7 +1162,7 @@ mod tests {
fn parse_multiple_mixed_arguments() {
p!("[val: 12pt, key=value]" =>
[func!("val": (Len(Length::pt(12.0))), { "key" => Id("value") })], [],
- [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ValidFuncName)],
+ [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ResolvedFunc)],
);
pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b") });
}
@@ -1197,7 +1179,7 @@ mod tests {
p!("[val: [hi]]" =>
[func!("val")],
[(0:6, 0:10, "expected argument, found function")],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
}
@@ -1208,7 +1190,7 @@ mod tests {
[func!("val": (Bool(true), Id("you")), {})],
[(0:10, 0:10, "expected comma"),
(0:10, 0:11, "expected argument, found equals sign")],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
// Unexpected equals.
@@ -1223,14 +1205,14 @@ mod tests {
[func!("val": (Id("key"), Num(12.0)), {})],
[(0:9, 0:9, "expected comma"),
(0:9, 0:10, "expected argument, found colon")],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
// Invalid colon after unkeyable positional argument.
p!("[val: true:12]" => [func!("val": (Bool(true), Num(12.0)), {})],
[(0:10, 0:10, "expected comma"),
(0:10, 0:11, "expected argument, found colon")],
- [(0:1, 0:4, ValidFuncName)],
+ [(0:1, 0:4, ResolvedFunc)],
);
}
@@ -1274,13 +1256,13 @@ mod tests {
// Space before function
p!(" [val]" =>
[(0:0, 0:1, S), (0:1, 0:6, func!((0:1, 0:4, "val")))], [],
- [(0:2, 0:5, ValidFuncName)],
+ [(0:2, 0:5, ResolvedFunc)],
);
// Newline before function
p!(" \n\r\n[val]" =>
[(0:0, 2:0, Parbreak), (2:0, 2:5, func!((0:1, 0:4, "val")))], [],
- [(2:1, 2:4, ValidFuncName)],
+ [(2:1, 2:4, ResolvedFunc)],
);
// Content before function
@@ -1293,7 +1275,7 @@ mod tests {
(0:19, 0:20, T("🌎"))
],
[],
- [(0:7, 0:10, ValidFuncName)],
+ [(0:7, 0:10, ResolvedFunc)],
);
// Nested function
@@ -1308,7 +1290,7 @@ mod tests {
]))
],
[],
- [(0:2, 0:5, ValidFuncName), (1:6, 1:9, ValidFuncName)],
+ [(0:2, 0:5, ResolvedFunc), (1:6, 1:9, ResolvedFunc)],
);
}
}
diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs
index 8fdad6a0..aac2b1b8 100644
--- a/src/syntax/scope.rs
+++ b/src/syntax/scope.rs
@@ -1,4 +1,4 @@
-//! Scopes containing function parsers.
+//! Mapping of function names to function parsers.
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
@@ -15,33 +15,31 @@ pub struct Scope {
impl Scope {
/// Create a new empty scope with a fallback parser that is invoked when no
/// match is found.
- pub fn new<F>() -> Scope
- where F: ParseCall<Meta=()> + DynamicNode + 'static {
- Scope {
+ pub fn new<F>() -> Self
+ where
+ F: ParseCall<Meta = ()> + DynamicNode + 'static
+ {
+ Self {
parsers: HashMap::new(),
fallback: make_parser::<F>(()),
}
}
- /// Create a new scope with the standard functions contained.
- pub fn with_std() -> Scope {
- crate::library::std()
- }
-
- /// Associate the given name with a type that is parseable into a function.
+ /// Associate the given function name with a dynamic node type.
pub fn add<F>(&mut self, name: &str)
- where F: ParseCall<Meta=()> + DynamicNode + 'static {
+ where
+ F: ParseCall<Meta = ()> + DynamicNode + 'static
+ {
self.add_with_meta::<F>(name, ());
}
- /// Add a parseable type with additional metadata that is given to the
- /// parser (other than the default of `()`).
+ /// Add a dynamic node type with additional metadata that is passed to the
+ /// parser.
pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseCall>::Meta)
- where F: ParseCall + DynamicNode + 'static {
- self.parsers.insert(
- name.to_string(),
- make_parser::<F>(metadata),
- );
+ where
+ F: ParseCall + DynamicNode + 'static
+ {
+ self.parsers.insert(name.to_string(), make_parser::<F>(metadata));
}
/// Return the parser with the given name if there is one.
@@ -57,14 +55,14 @@ impl Scope {
impl Debug for Scope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.debug_set()
- .entries(self.parsers.keys())
- .finish()
+ f.debug_set().entries(self.parsers.keys()).finish()
}
}
fn make_parser<F>(metadata: <F as ParseCall>::Meta) -> Box<CallParser>
-where F: ParseCall + DynamicNode + 'static {
+where
+ F: ParseCall + DynamicNode + 'static,
+{
Box::new(move |f, s| {
F::parse(f, s, metadata.clone())
.map(|tree| Box::new(tree) as Box<dyn DynamicNode>)
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 9b3c7d24..af293718 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -36,13 +36,13 @@ pub struct Spanned<T> {
impl<T> Spanned<T> {
/// Create a new instance from a value and its span.
- pub fn new(v: T, span: Span) -> Spanned<T> {
- Spanned { v, span }
+ pub fn new(v: T, span: Span) -> Self {
+ Self { v, span }
}
/// Create a new instance from a value with the zero span.
- pub fn zero(v: T) -> Spanned<T> {
- Spanned { v, span: Span::ZERO }
+ pub fn zero(v: T) -> Self {
+ Self { v, span: Span::ZERO }
}
/// Access the value.
@@ -51,12 +51,12 @@ impl<T> Spanned<T> {
}
/// Map the value using a function while keeping the span.
- pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V {
+ pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
Spanned { v: f(self.v), span: self.span }
}
/// Maps the span while keeping the value.
- pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span {
+ pub fn map_span(mut self, f: impl FnOnce(Span) -> Span) -> Self {
self.span = f(self.span);
self
}
@@ -91,35 +91,35 @@ pub struct Span {
impl Span {
/// The zero span.
- pub const ZERO: Span = Span { start: Pos::ZERO, end: Pos::ZERO };
+ pub const ZERO: Self = Self { start: Pos::ZERO, end: Pos::ZERO };
/// Create a new span from start and end positions.
- pub fn new(start: Pos, end: Pos) -> Span {
- Span { start, end }
+ pub fn new(start: Pos, end: Pos) -> Self {
+ Self { start, end }
}
/// Create a span including just a single position.
- pub fn at(pos: Pos) -> Span {
- Span { start: pos, end: pos }
+ pub fn at(pos: Pos) -> Self {
+ Self { start: pos, end: pos }
}
/// Create a new span with the earlier start and later end position.
- pub fn merge(a: Span, b: Span) -> Span {
- Span {
+ pub fn merge(a: Self, b: Self) -> Self {
+ Self {
start: a.start.min(b.start),
end: a.end.max(b.end),
}
}
/// Expand a span by merging it with another span.
- pub fn expand(&mut self, other: Span) {
- *self = Span::merge(*self, other)
+ pub fn expand(&mut self, other: Self) {
+ *self = Self::merge(*self, other)
}
}
impl Offset for Span {
fn offset(self, by: Pos) -> Self {
- Span {
+ Self {
start: self.start.offset(by),
end: self.end.offset(by),
}
@@ -144,31 +144,31 @@ pub struct Pos {
impl Pos {
/// The line 0, column 0 position.
- pub const ZERO: Pos = Pos { line: 0, column: 0 };
+ pub const ZERO: Self = Self { line: 0, column: 0 };
/// Create a new position from line and column.
- pub fn new(line: usize, column: usize) -> Pos {
- Pos { line, column }
+ pub fn new(line: usize, column: usize) -> Self {
+ Self { line, column }
}
}
impl Offset for Pos {
- fn offset(self, by: Pos) -> Self {
+ fn offset(self, by: Self) -> Self {
by + self
}
}
impl Add for Pos {
- type Output = Pos;
+ type Output = Self;
- fn add(self, rhs: Pos) -> Pos {
+ fn add(self, rhs: Self) -> Self {
if rhs.line == 0 {
- Pos {
+ Self {
line: self.line,
- column: self.column + rhs.column
+ column: self.column + rhs.column,
}
} else {
- Pos {
+ Self {
line: self.line + rhs.line,
column: rhs.column,
}
@@ -177,16 +177,16 @@ impl Add for Pos {
}
impl Sub for Pos {
- type Output = Pos;
+ type Output = Self;
- fn sub(self, rhs: Pos) -> Pos {
+ fn sub(self, rhs: Self) -> Self {
if self.line == rhs.line {
- Pos {
+ Self {
line: 0,
- column: self.column - rhs.column
+ column: self.column - rhs.column,
}
} else {
- Pos {
+ Self {
line: self.line - rhs.line,
column: self.column,
}
diff --git a/src/syntax/test.rs b/src/syntax/test.rs
index 504bc334..db7a2de2 100644
--- a/src/syntax/test.rs
+++ b/src/syntax/test.rs
@@ -2,15 +2,16 @@ use std::fmt::Debug;
use crate::func::parse_maybe_body;
use super::decoration::Decoration;
-use super::expr::{Expr, Ident, Tuple, NamedTuple, Object, Pair};
-use super::parsing::{FuncHeader, FuncArgs, FuncArg};
+use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple};
+use super::parsing::{FuncArg, FuncArgs, FuncHeader};
use super::span::Spanned;
use super::tokens::Token;
-use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
+use super::tree::{DynamicNode, SyntaxNode, SyntaxTree};
-/// Check whether the expected and found results are the same.
pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool)
-where T: Debug + PartialEq + SpanlessEq {
+where
+ T: Debug + PartialEq + SpanlessEq,
+{
let cmp = if cmp_spans { PartialEq::eq } else { SpanlessEq::spanless_eq };
if !cmp(&exp, &found) {
println!("source: {:?}", src);
@@ -41,7 +42,7 @@ macro_rules! span_vec {
}
macro_rules! span_item {
- (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({
+ (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => {{
use $crate::syntax::span::{Pos, Span, Spanned};
Spanned {
span: Span::new(
@@ -50,7 +51,7 @@ macro_rules! span_item {
),
v: $v
}
- });
+ }};
($v:expr) => {
$crate::syntax::span::Spanned::zero($v)
@@ -70,7 +71,7 @@ function! {
let cloned = header.clone();
header.args.pos.0.clear();
header.args.key.0.clear();
- DebugFn {
+ Self {
header: cloned,
body: parse_maybe_body(body, state, f),
}
@@ -80,7 +81,7 @@ function! {
}
/// Compares elements by only looking at values and ignoring spans.
-pub trait SpanlessEq<Rhs=Self> {
+pub trait SpanlessEq<Rhs = Self> {
fn spanless_eq(&self, other: &Rhs) -> bool;
}
@@ -102,20 +103,21 @@ impl SpanlessEq for SyntaxNode {
impl SpanlessEq for DebugFn {
fn spanless_eq(&self, other: &DebugFn) -> bool {
self.header.spanless_eq(&other.header)
- && self.body.spanless_eq(&other.body)
+ && self.body.spanless_eq(&other.body)
}
}
impl SpanlessEq for FuncHeader {
fn spanless_eq(&self, other: &Self) -> bool {
- self.name.spanless_eq(&other.name) && self.args.spanless_eq(&other.args)
+ self.name.spanless_eq(&other.name)
+ && self.args.spanless_eq(&other.args)
}
}
impl SpanlessEq for FuncArgs {
fn spanless_eq(&self, other: &Self) -> bool {
self.key.spanless_eq(&other.key)
- && self.pos.spanless_eq(&other.pos)
+ && self.pos.spanless_eq(&other.pos)
}
}
@@ -154,7 +156,7 @@ impl SpanlessEq for Tuple {
impl SpanlessEq for NamedTuple {
fn spanless_eq(&self, other: &NamedTuple) -> bool {
self.name.v == other.name.v
- && self.tuple.v.spanless_eq(&other.tuple.v)
+ && self.tuple.v.spanless_eq(&other.tuple.v)
}
}
@@ -173,7 +175,7 @@ impl SpanlessEq for Pair {
impl<T: SpanlessEq> SpanlessEq for Vec<T> {
fn spanless_eq(&self, other: &Vec<T>) -> bool {
self.len() == other.len()
- && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y))
+ && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y))
}
}
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index 1ea11449..ef249471 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -9,7 +9,6 @@ use super::span::{Pos, Span, Spanned};
use Token::*;
use TokenMode::*;
-
/// A minimal semantic entity of source code.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Token<'s> {
@@ -71,14 +70,14 @@ pub enum Token<'s> {
/// a String. The escaping is done later in the parser.
string: &'s str,
/// Whether the closing quote was present.
- terminated: bool
+ terminated: bool,
},
+ /// A boolean in a function header: `true | false`.
+ ExprBool(bool),
/// A number in a function header: `3.14`.
ExprNumber(f64),
/// A length in a function header: `12pt`.
ExprLength(Length),
- /// A boolean in a function header: `true | false`.
- ExprBool(bool),
/// A hex value in a function header: `#20d82a`.
ExprHex(&'s str),
/// A plus in a function header, signifying the addition of expressions.
@@ -130,9 +129,9 @@ impl<'s> Token<'s> {
Equals => "equals sign",
ExprIdent(_) => "identifier",
ExprStr { .. } => "string",
+ ExprBool(_) => "bool",
ExprNumber(_) => "number",
ExprLength(_) => "length",
- ExprBool(_) => "bool",
ExprHex(_) => "hex value",
Plus => "plus",
Hyphen => "minus",
@@ -173,8 +172,8 @@ impl<'s> Tokens<'s> {
///
/// The first token's span starts an the given `offset` position instead of
/// the zero position.
- pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Tokens<'s> {
- Tokens {
+ pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Self {
+ Self {
src,
mode,
iter: src.chars().peekable(),
@@ -200,7 +199,7 @@ impl<'s> Iterator for Tokens<'s> {
type Item = Spanned<Token<'s>>;
/// Parse the next token in the source code.
- fn next(&mut self) -> Option<Spanned<Token<'s>>> {
+ fn next(&mut self) -> Option<Self::Item> {
let start = self.pos();
let first = self.eat()?;
@@ -366,7 +365,7 @@ impl<'s> Tokens<'s> {
}
let end = self.index();
- (&self.src[start .. end], terminated)
+ (&self.src[start..end], terminated)
}
fn read_string(&mut self) -> Token<'s> {
@@ -404,7 +403,7 @@ impl<'s> Tokens<'s> {
Some(c) if is_escapable(c) => {
let index = self.index();
self.eat();
- Text(&self.src[index .. index + c.len_utf8()])
+ Text(&self.src[index..index + c.len_utf8()])
}
Some(c) if c.is_whitespace() => Backslash,
Some(_) => Text("\\"),
@@ -442,13 +441,13 @@ impl<'s> Tokens<'s> {
/// Returns the string from the index where this was called offset by
/// `offset_start` to the end offset by `offset_end`. The end is before or
/// after the match depending on `eat_match`.
- fn read_string_until<F>(
+ fn read_string_until(
&mut self,
- mut f: F,
+ mut f: impl FnMut(char) -> bool,
eat_match: bool,
offset_start: isize,
offset_end: isize,
- ) -> (&'s str, bool) where F: FnMut(char) -> bool {
+ ) -> (&'s str, bool) {
let start = ((self.index() as isize) + offset_start) as usize;
let mut matched = false;
@@ -469,7 +468,7 @@ impl<'s> Tokens<'s> {
end = ((end as isize) + offset_end) as usize;
}
- (&self.src[start .. end], matched)
+ (&self.src[start..end], matched)
}
fn eat(&mut self) -> Option<char> {
@@ -493,7 +492,7 @@ impl<'s> Tokens<'s> {
fn parse_percentage(text: &str) -> Option<f64> {
if text.ends_with('%') {
- text[.. text.len() - 1].parse::<f64>().ok()
+ text[..text.len() - 1].parse::<f64>().ok()
} else {
None
}
@@ -503,7 +502,7 @@ fn parse_percentage(text: &str) -> Option<f64> {
pub fn is_newline_char(character: char) -> bool {
match character {
// Line Feed, Vertical Tab, Form Feed, Carriage Return.
- '\x0A' ..= '\x0D' => true,
+ '\x0A'..='\x0D' => true,
// Next Line, Line Separator, Paragraph Separator.
'\u{0085}' | '\u{2028}' | '\u{2029}' => true,
_ => false,
@@ -544,15 +543,15 @@ mod tests {
LeftParen as LP, RightParen as RP,
LeftBrace as LB, RightBrace as RB,
ExprIdent as Id,
+ ExprBool as Bool,
ExprNumber as Num,
ExprLength as Len,
- ExprBool as Bool,
ExprHex as Hex,
- Text as T,
Plus,
Hyphen as Min,
- Star,
Slash,
+ Star,
+ Text as T,
};
/// Test whether the given string tokenizes into the given list of tokens.
diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs
index 41a03fae..28997e7c 100644
--- a/src/syntax/tree.rs
+++ b/src/syntax/tree.rs
@@ -6,7 +6,7 @@ use std::fmt::Debug;
use crate::layout::Layout;
use super::span::SpanVec;
-/// A list of nodes which forms a tree together with the nodes' children.
+/// A collection of nodes which form a tree together with the nodes' children.
pub type SyntaxTree = SpanVec<SyntaxNode>;
/// A syntax node, which encompasses a single logical entity of parsed source
@@ -27,7 +27,7 @@ pub enum SyntaxNode {
ToggleItalic,
/// Bolder was enabled / disabled.
ToggleBolder,
- /// A subtree, typically a function invocation.
+ /// A dynamic node, create through function invocations in source code.
Dyn(Box<dyn DynamicNode>),
}
@@ -65,13 +65,16 @@ pub trait DynamicNode: Debug + Layout {
impl dyn DynamicNode {
/// Downcast this dynamic node to a concrete node.
- pub fn downcast<N>(&self) -> Option<&N> where N: DynamicNode + 'static {
- self.as_any().downcast_ref::<N>()
+ pub fn downcast<T>(&self) -> Option<&T>
+ where
+ T: DynamicNode + 'static,
+ {
+ self.as_any().downcast_ref::<T>()
}
}
impl PartialEq for dyn DynamicNode {
- fn eq(&self, other: &dyn DynamicNode) -> bool {
+ fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other)
}
}
@@ -82,7 +85,10 @@ impl Clone for Box<dyn DynamicNode> {
}
}
-impl<T> DynamicNode for T where T: Debug + PartialEq + Clone + Layout + 'static {
+impl<T> DynamicNode for T
+where
+ T: Debug + PartialEq + Clone + Layout + 'static,
+{
fn as_any(&self) -> &dyn Any {
self
}
diff --git a/src/syntax/value.rs b/src/syntax/value.rs
index 5c264ca0..b7211eaf 100644
--- a/src/syntax/value.rs
+++ b/src/syntax/value.rs
@@ -2,18 +2,20 @@
use fontdock::{FontStyle, FontWeight, FontWidth};
-use crate::Feedback;
use crate::layout::prelude::*;
use crate::length::{Length, ScaleLength};
use crate::paper::Paper;
-use super::span::Spanned;
+use crate::Feedback;
use super::expr::*;
+use super::span::Spanned;
/// Value types are used to extract values from functions, tuples and
/// objects. They represent the value part of an argument.
+///
+/// # Example
/// ```typst
-/// [func: value, key=value]
-/// ^^^^^ ^^^^^
+/// [func: 12pt, key="these are both values"]
+/// ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
/// ```
pub trait Value: Sized {
/// Try to parse this value from an expression.
@@ -53,8 +55,8 @@ macro_rules! match_value {
match_value!(Expr, "expression", e => e);
match_value!(Ident, "identifier", Expr::Ident(i) => i);
match_value!(String, "string", Expr::Str(s) => s);
-match_value!(f64, "number", Expr::Number(n) => n);
match_value!(bool, "bool", Expr::Bool(b) => b);
+match_value!(f64, "number", Expr::Number(n) => n);
match_value!(Length, "length", Expr::Length(l) => l);
match_value!(Tuple, "tuple", Expr::Tuple(t) => t);
match_value!(Object, "object", Expr::Object(o) => o);
@@ -63,7 +65,7 @@ match_value!(ScaleLength, "number or length",
Expr::Number(scale) => ScaleLength::Scaled(scale),
);
-/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements
+/// A value type that matches identifiers and strings and implements
/// `Into<String>`.
pub struct StringLike(pub String);
@@ -101,7 +103,7 @@ macro_rules! ident_value {
}
ident_value!(Dir, "direction", |s| match s {
- "ltr" => Some(LTT),
+ "ltr" => Some(LTR),
"rtl" => Some(RTL),
"ttb" => Some(TTB),
"btt" => Some(BTT),
@@ -109,11 +111,11 @@ ident_value!(Dir, "direction", |s| match s {
});
ident_value!(SpecAlign, "alignment", |s| match s {
- "left" => Some(SpecAlign::Left),
- "right" => Some(SpecAlign::Right),
- "top" => Some(SpecAlign::Top),
- "bottom" => Some(SpecAlign::Bottom),
- "center" => Some(SpecAlign::Center),
+ "left" => Some(Self::Left),
+ "right" => Some(Self::Right),
+ "top" => Some(Self::Top),
+ "bottom" => Some(Self::Bottom),
+ "center" => Some(Self::Center),
_ => None,
});
@@ -127,7 +129,7 @@ impl Value for FontWeight {
const MIN: u16 = 100;
const MAX: u16 = 900;
- Some(FontWeight(if weight < MIN as f64 {
+ Some(Self(if weight < MIN as f64 {
error!(@f, expr.span, "the minimum font weight is {}", MIN);
MIN
} else if weight > MAX as f64 {
@@ -163,7 +165,7 @@ impl Value for FontWidth {
const MIN: u16 = 1;
const MAX: u16 = 9;
- FontWidth::new(if width < MIN as f64 {
+ Self::new(if width < MIN as f64 {
error!(@f, expr.span, "the minimum font width is {}", MIN);
MIN
} else if width > MAX as f64 {
diff --git a/tests/test_typeset.rs b/tests/test_typeset.rs
index 6ac34c85..880818da 100644
--- a/tests/test_typeset.rs
+++ b/tests/test_typeset.rs
@@ -5,21 +5,21 @@ use std::fs::{self, File};
use std::io::BufWriter;
use std::rc::Rc;
+use fontdock::fs::{FsIndex, FsProvider};
+use fontdock::FontLoader;
use futures_executor::block_on;
-use raqote::{DrawTarget, Source, SolidSource, PathBuilder, Vector, Transform};
+use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
use ttf_parser::OutlineBuilder;
-use typstc::Typesetter;
+use typstc::export::pdf;
use typstc::font::{DynProvider, SharedFontLoader};
use typstc::geom::{Size, Value4};
-use typstc::layout::MultiLayout;
use typstc::layout::elements::{LayoutElement, Shaped};
+use typstc::layout::MultiLayout;
use typstc::length::Length;
-use typstc::style::PageStyle;
use typstc::paper::PaperClass;
-use typstc::export::pdf;
-use fontdock::FontLoader;
-use fontdock::fs::{FsIndex, FsProvider};
+use typstc::style::PageStyle;
+use typstc::Typesetter;
const TEST_DIR: &str = "tests";
const OUT_DIR: &str = "tests/out";
@@ -38,12 +38,7 @@ fn main() {
continue;
}
- let name = path
- .file_stem()
- .unwrap()
- .to_string_lossy()
- .to_string();
-
+ let name = path.file_stem().unwrap().to_string_lossy().to_string();
if filter.matches(&name) {
let src = fs::read_to_string(&path).unwrap();
filtered.push((name, src));
@@ -93,18 +88,15 @@ fn test(
let typeset = block_on(typesetter.typeset(src));
let layouts = typeset.output;
for diagnostic in typeset.feedback.diagnostics {
- println!(" {:?} {:?}: {}",
- diagnostic.v.level,
- diagnostic.span,
- diagnostic.v.message
+ println!(
+ " {:?} {:?}: {}",
+ diagnostic.v.level, diagnostic.span, diagnostic.v.message,
);
}
- // Render the PNG file.
let png_path = format!("{}/{}.png", OUT_DIR, name);
render(&layouts, &loader, 3.0).write_png(png_path).unwrap();
- // Write the PDF file.
let pdf_path = format!("{}/{}.pdf", OUT_DIR, name);
let file = BufWriter::new(File::create(pdf_path).unwrap());
pdf::export(&layouts, &loader, file).unwrap();
@@ -116,19 +108,19 @@ struct TestFilter {
}
impl TestFilter {
- fn new(args: impl Iterator<Item=String>) -> TestFilter {
+ fn new(args: impl Iterator<Item = String>) -> Self {
let mut filter = Vec::new();
let mut perfect = false;
for arg in args {
match arg.as_str() {
- "--nocapture" => {},
+ "--nocapture" => {}
"=" => perfect = true,
_ => filter.push(arg),
}
}
- TestFilter { filter, perfect }
+ Self { filter, perfect }
}
fn matches(&self, name: &str) -> bool {
@@ -136,7 +128,7 @@ impl TestFilter {
self.filter.iter().any(|p| name == p)
} else {
self.filter.is_empty()
- || self.filter.iter().any(|p| name.contains(p))
+ || self.filter.iter().any(|p| name.contains(p))
}
}
}
@@ -174,15 +166,13 @@ fn render(
for &(pos, ref element) in &layout.elements.0 {
match element {
- LayoutElement::Text(shaped) => {
- render_shaped(
- &mut surface,
- loader,
- shaped,
- scale * pos + offset,
- scale,
- );
- },
+ LayoutElement::Text(shaped) => render_shaped(
+ &mut surface,
+ loader,
+ shaped,
+ scale * pos + offset,
+ scale,
+ ),
}
}
@@ -215,7 +205,7 @@ fn render_shaped(
let t = Transform::create_scale(s as f32, -s as f32)
.post_translate(Vector::new(x as f32, y as f32));
- surface.fill(
+ surface.fill(
&path.transform(&t),
&Source::Solid(SolidSource { r: 0, g: 0, b: 0, a: 255 }),
&Default::default(),