Merge pull request #13 from typst/fmt

Formatting with rustfmt
This commit is contained in:
Martin 2020-08-31 11:58:15 +02:00 committed by GitHub
commit 696560622d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 384 additions and 375 deletions

View File

@ -15,14 +15,10 @@ const FONT_DIR: &str = "fonts";
const COMA: &str = include_str!("../tests/coma.typ");
fn parsing_benchmark(c: &mut Criterion) {
c.bench_function("parse-coma-28-lines", |b| {
b.iter(|| parse(COMA))
});
c.bench_function("parse-coma-28-lines", |b| b.iter(|| parse(COMA)));
let long = COMA.repeat(100);
c.bench_function("parse-coma-2800-lines", |b| {
b.iter(|| parse(&long))
});
c.bench_function("parse-coma-2800-lines", |b| b.iter(|| parse(&long)));
}
fn typesetting_benchmark(c: &mut Criterion) {

View File

@ -30,8 +30,7 @@ fn main() {
panic!("source and destination path are the same");
}
let src = read_to_string(src_path)
.expect("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");
@ -55,16 +54,16 @@ fn main() {
"{}: {}:{}:{} - {}:{}: {}",
format!("{:?}", diagnostic.v.level).to_lowercase(),
src_path.display(),
span.start.line + 1, span.start.column + 1,
span.end.line + 1, span.end.column + 1,
span.start.line + 1,
span.start.column + 1,
span.end.line + 1,
span.end.column + 1,
diagnostic.v.message,
);
}
let file = File::create(&dest_path)
.expect("failed to create output file");
let file = File::create(&dest_path).expect("failed to create output file");
let writer = BufWriter::new(file);
pdf::export(&layouts, &loader, writer)
.expect("failed to export pdf");
pdf::export(&layouts, &loader, writer).expect("failed to export pdf");
}

10
rustfmt.toml Normal file
View File

@ -0,0 +1,10 @@
unstable_features = true
overflow_delimited_expr = true
spaces_around_ranges = true
use_field_init_shorthand = true
max_width = 90
struct_lit_width = 40
chain_width = 70
single_line_if_else_max_width = 60

View File

@ -49,7 +49,7 @@ impl FromStr for RgbaColor {
}
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;
@ -59,13 +59,12 @@ impl FromStr for RgbaColor {
let mut values: [u8; 4] = [255; 4];
for elem in if alpha { 0..4 } else { 0..3 } {
for elem in if alpha { 0 .. 4 } else { 0 .. 3 } {
let item_len = if long { 2 } else { 1 };
let pos = elem * item_len;
let item = &hex_str[pos..(pos+item_len)];
values[elem] = u8::from_str_radix(item, 16)
.map_err(|_| ParseColorError)?;
let item = &hex_str[pos .. (pos + item_len)];
values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseColorError)?;
if short {
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
@ -81,12 +80,14 @@ 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,
)?;
}
@ -116,10 +117,7 @@ mod tests {
#[test]
fn parse_color_strings() {
fn test(hex: &str, r: u8, g: u8, b: u8, a: u8) {
assert_eq!(
RgbaColor::from_str(hex),
Ok(RgbaColor::new(r, g, b, a)),
);
assert_eq!(RgbaColor::from_str(hex), Ok(RgbaColor::new(r, g, b, a)));
}
test("f61243ff", 0xf6, 0x12, 0x43, 0xff);

View File

@ -14,9 +14,7 @@ impl Scope {
// Create a new empty scope with a fallback function that is invoked when no
// match is found.
pub fn new() -> Self {
Self {
functions: HashMap::new(),
}
Self { functions: HashMap::new() }
}
/// Associate the given name with the function.

View File

@ -117,7 +117,8 @@ impl<V> Table<V> {
/// Iterator over all borrowed keys and values.
pub fn iter(&self) -> impl Iterator<Item = (BorrowedKey, &V)> {
self.nums().map(|(&k, v)| (BorrowedKey::Num(k), v))
self.nums()
.map(|(&k, v)| (BorrowedKey::Num(k), v))
.chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v)))
}
@ -138,13 +139,17 @@ impl<V> Table<V> {
/// Move into an owned iterator over owned keys and values.
pub fn into_iter(self) -> impl Iterator<Item = (OwnedKey, V)> {
self.nums.into_iter().map(|(k, v)| (OwnedKey::Num(k), v))
self.nums
.into_iter()
.map(|(k, v)| (OwnedKey::Num(k), v))
.chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(k), v)))
}
/// Move into an owned iterator over all values in the table.
pub fn into_values(self) -> impl Iterator<Item = V> {
self.nums.into_iter().map(|(_, v)| v)
self.nums
.into_iter()
.map(|(_, v)| v)
.chain(self.strs.into_iter().map(|(_, v)| v))
}

View File

@ -6,15 +6,15 @@ use std::rc::Rc;
use fontdock::{FontStyle, FontWeight, FontWidth};
use super::table::{SpannedEntry, Table};
use crate::color::RgbaColor;
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
use crate::length::{Length, ScaleLength};
use crate::paper::Paper;
use crate::syntax::span::{Span, Spanned};
use crate::syntax::tree::{SyntaxTree, SyntaxNode};
use crate::syntax::tree::{SyntaxNode, SyntaxTree};
use crate::syntax::Ident;
use crate::{DynFuture, Feedback, Pass};
use super::table::{SpannedEntry, Table};
/// A computational value.
#[derive(Clone)]
@ -78,9 +78,10 @@ impl Spanned<Value> {
for entry in table.into_values() {
if let Some(last_end) = end {
let span = Span::new(last_end, entry.key.start);
commands.push(Command::LayoutSyntaxTree(vec![
Spanned::new(SyntaxNode::Spacing, span)
]));
commands.push(Command::LayoutSyntaxTree(vec![Spanned::new(
SyntaxNode::Spacing,
span,
)]));
}
end = Some(entry.val.span.end);
@ -90,14 +91,10 @@ impl Spanned<Value> {
}
// Format with debug.
val => vec![
Command::LayoutSyntaxTree(vec![
Spanned::new(
SyntaxNode::Text(format!("{:?}", val)),
self.span,
)
])
],
val => vec![Command::LayoutSyntaxTree(vec![Spanned::new(
SyntaxNode::Text(format!("{:?}", val)),
self.span,
)])],
}
}
}
@ -149,9 +146,8 @@ impl PartialEq for Value {
/// layouting engine to do what the function pleases.
///
/// The dynamic function object is wrapped in an `Rc` to keep `Value` clonable.
pub type FuncValue = Rc<
dyn Fn(Span, TableValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>
>;
pub type FuncValue =
Rc<dyn Fn(Span, TableValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>>;
/// A table of values.
///
@ -500,7 +496,10 @@ mod tests {
table.expect::<String>("", Span::ZERO, &mut f),
Some("hi".to_string())
);
assert_eq!(f.diagnostics, [error!(Span::ZERO, "expected string, found bool")]);
assert_eq!(f.diagnostics, [error!(
Span::ZERO,
"expected string, found bool"
)]);
assert_eq!(table.len(), 1);
}
@ -512,7 +511,10 @@ mod tests {
table.insert("hi", entry(Value::Bool(true)));
assert_eq!(table.take::<bool>(), Some(false));
assert_eq!(table.take_key::<f64>("hi", &mut f), None);
assert_eq!(f.diagnostics, [error!(Span::ZERO, "expected number, found bool")]);
assert_eq!(f.diagnostics, [error!(
Span::ZERO,
"expected number, found bool"
)]);
assert!(table.is_empty());
}
@ -522,10 +524,10 @@ mod tests {
table.insert(1, entry(Value::Bool(false)));
table.insert(3, entry(Value::Number(0.0)));
table.insert(7, entry(Value::Bool(true)));
assert_eq!(
table.take_all_num::<bool>().collect::<Vec<_>>(),
[(1, false), (7, true)],
);
assert_eq!(table.take_all_num::<bool>().collect::<Vec<_>>(), [
(1, false),
(7, true)
],);
assert_eq!(table.len(), 1);
assert_eq!(table[3].val.v, Value::Number(0.0));
}

View File

@ -7,8 +7,8 @@ use fontdock::FaceId;
use tide::content::Content;
use tide::doc::{Catalog, Page, PageTree, Resource, Text};
use tide::font::{
CIDFont, CIDFontType, CIDSystemInfo, CMap, CMapEncoding, FontDescriptor,
FontFlags, FontStream, GlyphUnit, Type0Font, WidthRecord,
CIDFont, CIDFontType, CIDSystemInfo, CMap, CMapEncoding, FontDescriptor, FontFlags,
FontStream, GlyphUnit, Type0Font, WidthRecord,
};
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
use ttf_parser::{name_id, GlyphId};
@ -89,20 +89,18 @@ impl<'a, W: Write> PdfExporter<'a, W> {
fn write_preface(&mut self) -> io::Result<()> {
// The document catalog.
self.writer.write_obj(self.offsets.catalog, &Catalog::new(self.offsets.page_tree))?;
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| {
Resource::Font(i + 1, start + (NUM_OBJECTS_PER_FONT * i))
});
let fonts = (0 .. self.to_pdf.len() as u32)
.map(|i| Resource::Font(i + 1, start + (NUM_OBJECTS_PER_FONT * i)));
// The root page tree.
self.writer.write_obj(
self.offsets.page_tree,
PageTree::new()
.kids(ids(self.offsets.pages))
.resources(fonts),
PageTree::new().kids(ids(self.offsets.pages)).resources(fonts),
)?;
// The page objects (non-root nodes in the page tree).
@ -119,9 +117,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
self.writer.write_obj(
page_id,
Page::new(self.offsets.page_tree)
.media_box(rect)
.content(content_id),
Page::new(self.offsets.page_tree).media_box(rect).content(content_id),
)?;
}
@ -151,7 +147,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
size = shaped.size;
text.tf(
self.to_pdf[&shaped.face] as u32 + 1,
Length::raw(size).as_pt() as f32
Length::raw(size).as_pt() as f32,
);
}
@ -178,8 +174,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
let name = face
.names()
.find(|entry| {
entry.name_id() == name_id::POST_SCRIPT_NAME
&& entry.is_unicode()
entry.name_id() == name_id::POST_SCRIPT_NAME && entry.is_unicode()
})
.map(|entry| entry.to_string())
.flatten()
@ -190,9 +185,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
let units_per_em = face.units_per_em().unwrap_or(1000) as f64;
let ratio = 1.0 / units_per_em;
let to_glyph_unit = |font_unit: f64| {
(1000.0 * ratio * font_unit).round() as GlyphUnit
};
let to_glyph_unit =
|font_unit: f64| (1000.0 * ratio * font_unit).round() as GlyphUnit;
let global_bbox = face.global_bounding_box();
let bbox = Rect::new(
@ -218,7 +212,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
flags.insert(FontFlags::SMALL_CAP);
let num_glyphs = face.number_of_glyphs();
let widths: Vec<_> = (0..num_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();
@ -258,23 +252,21 @@ impl<'a, W: Write> PdfExporter<'a, W> {
)?;
// Write the font descriptor (contains metrics about the font).
self.writer.write_obj(id + 2,
self.writer.write_obj(
id + 2,
FontDescriptor::new(base_font, flags, italic_angle)
.font_bbox(bbox)
.ascent(to_glyph_unit(ascender as f64))
.descent(to_glyph_unit(descender as f64))
.cap_height(to_glyph_unit(cap_height as f64))
.stem_v(stem_v as GlyphUnit)
.font_file_2(id + 4)
.font_file_2(id + 4),
)?;
// 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,
))?;
self.writer
.write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?;
// Write the face's bytes.
self.writer.write_obj(id + 4, &FontStream::new(face.data()))?;
@ -328,5 +320,5 @@ fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
}
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
start..=end
start ..= end
}

View File

@ -30,9 +30,8 @@ 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
// it can't be touched from outside this type.
let slice: &'static [u8] = unsafe {
std::slice::from_raw_parts(vec.as_ptr(), vec.len())
};
let slice: &'static [u8] =
unsafe { std::slice::from_raw_parts(vec.as_ptr(), vec.len()) };
Some(Self {
data: vec,

View File

@ -21,12 +21,18 @@ impl<T: Clone> Value2<T> {
}
/// Create a new 2D-value with `x` set to a value and `y` to default.
pub fn with_x(x: T) -> Self where 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) -> Self where T: Default {
pub fn with_y(y: T) -> Self
where
T: Default,
{
Self { x: T::default(), y }
}
@ -53,22 +59,38 @@ impl<T: Clone> Value2<T> {
/// Return the primary value of this specialized 2D-value.
pub fn primary(self, axes: LayoutAxes) -> T {
if axes.primary.axis() == Horizontal { self.x } else { self.y }
if axes.primary.axis() == Horizontal {
self.x
} else {
self.y
}
}
/// Borrow the primary value of this specialized 2D-value mutably.
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y }
if axes.primary.axis() == Horizontal {
&mut self.x
} else {
&mut self.y
}
}
/// Return the secondary value of this specialized 2D-value.
pub fn secondary(self, axes: LayoutAxes) -> T {
if axes.primary.axis() == Horizontal { self.y } else { self.x }
if axes.primary.axis() == Horizontal {
self.y
} else {
self.x
}
}
/// Borrow the secondary value of this specialized 2D-value mutably.
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x }
if axes.primary.axis() == Horizontal {
&mut self.y
} else {
&mut self.x
}
}
/// Returns the generalized version of a `Size2D` dependent on the layouting
@ -98,10 +120,7 @@ impl<T: Clone> Value2<T> {
impl<T: Debug> Debug for Value2<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_list()
.entry(&self.x)
.entry(&self.y)
.finish()
f.debug_list().entry(&self.x).entry(&self.y).finish()
}
}
@ -159,10 +178,7 @@ impl Neg for Size {
type Output = Size;
fn neg(self) -> Size {
Size {
x: -self.x,
y: -self.y,
}
Size { x: -self.x, y: -self.y }
}
}

View File

@ -246,9 +246,7 @@ impl LineLayouter {
for (offset, layout) in layouts {
let x = match self.ctx.axes.primary.is_positive() {
true => offset,
false => self.run.size.x
- offset
- layout.size.primary(self.ctx.axes),
false => self.run.size.x - offset - layout.size.primary(self.ctx.axes),
};
let pos = Size::with_x(x);
@ -258,7 +256,7 @@ 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
elements,
});
self.run = LineRun::new();

View File

@ -10,7 +10,7 @@ mod tree;
/// Basic types used across the layouting engine.
pub mod prelude {
pub use super::primitive::*;
pub use super::{BoxLayout, layout, LayoutContext, LayoutSpace, MultiLayout};
pub use super::{layout, BoxLayout, LayoutContext, LayoutSpace, MultiLayout};
pub use Dir::*;
pub use GenAlign::*;
pub use GenAxis::*;

View File

@ -140,7 +140,11 @@ pub enum SpecAxis {
impl SpecAxis {
/// The generic version of this axis in the given system of axes.
pub fn to_generic(self, axes: LayoutAxes) -> GenAxis {
if self == axes.primary.axis() { Primary } else { Secondary }
if self == axes.primary.axis() {
Primary
} else {
Secondary
}
}
}
@ -240,7 +244,11 @@ impl SpecAlign {
pub fn to_generic(self, axes: LayoutAxes) -> GenAlign {
let get = |spec: SpecAxis, align: GenAlign| {
let axis = spec.to_generic(axes);
if axes.get(axis).is_positive() { align } else { align.inv() }
if axes.get(axis).is_positive() {
align
} else {
align.inv()
}
};
match self {

View File

@ -19,8 +19,8 @@
//! The position of the first aligned box thus depends on the length of the
//! sentence in the second box.
use crate::geom::Value4;
use super::*;
use crate::geom::Value4;
/// Performs the stack layouting.
pub struct StackLayouter {
@ -130,14 +130,11 @@ 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;
}
@ -179,8 +176,7 @@ impl StackLayouter {
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
}
@ -226,7 +222,7 @@ impl StackLayouter {
/// if no space is capable of that.
pub fn skip_to_fitting_space(&mut self, size: Size) {
let start = self.next_space();
for (index, space) in self.ctx.spaces[start..].iter().enumerate() {
for (index, space) in self.ctx.spaces[start ..].iter().enumerate() {
if space.usable().fits(size) {
self.finish_space(true);
self.start_space(start + index, true);
@ -246,7 +242,7 @@ impl StackLayouter {
expansion: LayoutExpansion::new(false, false),
}];
for space in &self.ctx.spaces[self.next_space()..] {
for space in &self.ctx.spaces[self.next_space() ..] {
spaces.push(space.inner());
}
@ -288,8 +284,12 @@ impl StackLayouter {
// expand if necessary.)
let usable = space.usable();
if space.expansion.horizontal { self.space.size.x = usable.x; }
if space.expansion.vertical { self.space.size.y = usable.y; }
if space.expansion.horizontal {
self.space.size.x = usable.x;
}
if space.expansion.vertical {
self.space.size.y = usable.y;
}
let size = self.space.size.padded(space.padding);
@ -347,8 +347,7 @@ 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);
@ -369,9 +368,8 @@ impl StackLayouter {
// The space in which this layout is aligned is given by the
// distances between the borders of it's bounding box.
let usable =
Size::new(bound.right - bound.left, bound.bottom - bound.top)
.generalized(axes);
let usable = Size::new(bound.right - bound.left, bound.bottom - bound.top)
.generalized(axes);
let local = usable.anchor(align, axes) - size.anchor(align, axes);
let pos = Size::new(bound.left, bound.top) + local.specialized(axes);
@ -379,11 +377,7 @@ impl StackLayouter {
elements.extend_offset(pos, layout.elements);
}
self.layouts.push(BoxLayout {
size,
align: self.ctx.align,
elements,
});
self.layouts.push(BoxLayout { size, align: self.ctx.align, elements });
// ------------------------------------------------------------------ //
// Step 5: Start the next space.

View File

@ -7,11 +7,11 @@
use fontdock::{FaceId, FaceQuery, FontStyle};
use ttf_parser::GlyphId;
use super::elements::{LayoutElement, Shaped};
use super::*;
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 {

View File

@ -1,19 +1,16 @@
//! Layouting of syntax trees.
use crate::style::LayoutStyle;
use crate::syntax::decoration::Decoration;
use crate::syntax::span::{Span, Spanned};
use crate::syntax::tree::{CallExpr, SyntaxNode, SyntaxTree, Code};
use crate::{DynFuture, Feedback, Pass};
use super::line::{LineContext, LineLayouter};
use super::text::{layout_text, TextContext};
use super::*;
use crate::style::LayoutStyle;
use crate::syntax::decoration::Decoration;
use crate::syntax::span::{Span, Spanned};
use crate::syntax::tree::{CallExpr, Code, SyntaxNode, SyntaxTree};
use crate::{DynFuture, Feedback, Pass};
/// Layout a syntax tree into a collection of boxes.
pub async fn layout_tree(
tree: &SyntaxTree,
ctx: LayoutContext<'_>,
) -> Pass<MultiLayout> {
pub async fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
let mut layouter = TreeLayouter::new(ctx);
layouter.layout_tree(tree).await;
layouter.finish()
@ -75,8 +72,12 @@ impl<'a> TreeLayouter<'a> {
}
SyntaxNode::Text(text) => {
if self.style.text.italic { decorate(self, Decoration::Italic); }
if self.style.text.bolder { decorate(self, Decoration::Bold); }
if self.style.text.italic {
decorate(self, Decoration::Italic);
}
if self.style.text.bolder {
decorate(self, Decoration::Bold);
}
self.layout_text(text).await;
}
@ -90,10 +91,8 @@ impl<'a> TreeLayouter<'a> {
}
fn layout_space(&mut self) {
self.layouter.add_primary_spacing(
self.style.text.word_spacing(),
SpacingKind::WORD,
);
self.layouter
.add_primary_spacing(self.style.text.word_spacing(), SpacingKind::WORD);
}
fn layout_parbreak(&mut self) {
@ -105,24 +104,20 @@ impl<'a> TreeLayouter<'a> {
async fn layout_text(&mut self, text: &str) {
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
layout_text(text, TextContext {
loader: &self.ctx.loader,
style: &self.style.text,
dir: self.ctx.axes.primary,
align: self.ctx.align,
})
.await,
);
}
async fn layout_raw(&mut self, lines: &[String]) {
// 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();
let mut first = true;
@ -176,7 +171,7 @@ impl<'a> TreeLayouter<'a> {
AddSpacing(space, kind, axis) => match axis {
Primary => self.layouter.add_primary_spacing(space, kind),
Secondary => self.layouter.add_secondary_spacing(space, kind),
}
},
BreakLine => self.layouter.finish_line(),
BreakPage => {
@ -203,13 +198,14 @@ impl<'a> TreeLayouter<'a> {
// new page style and update it within the layouter.
let margins = style.margins();
self.ctx.base = style.size.unpadded(margins);
self.layouter.set_spaces(vec![
LayoutSpace {
self.layouter.set_spaces(
vec![LayoutSpace {
size: style.size,
padding: margins,
expansion: LayoutExpansion::new(true, true),
}
], true);
}],
true,
);
} else {
error!(
@self.feedback, span,

View File

@ -150,7 +150,7 @@ impl FromStr for Length {
// have valid ASCII chars as subbytes.
let split = len - 2;
let bytes = src.as_bytes();
let unit = match &bytes[split..] {
let unit = match &bytes[split ..] {
b"pt" => Unit::Pt,
b"mm" => Unit::Mm,
b"cm" => Unit::Cm,
@ -158,7 +158,7 @@ impl FromStr for Length {
_ => return Err(ParseLengthError),
};
src[..split]
src[.. split]
.parse::<f64>()
.map(|val| Self::new(val, unit))
.map_err(|_| ParseLengthError)

View File

@ -95,24 +95,22 @@ impl Typesetter {
use crate::layout::prelude::*;
let margins = self.style.page.margins();
layout(
&tree,
LayoutContext {
loader: &self.loader,
scope: &self.std,
style: &self.style,
base: self.style.page.size.unpadded(margins),
spaces: vec![LayoutSpace {
size: self.style.page.size,
padding: margins,
expansion: LayoutExpansion::new(true, true),
}],
repeat: true,
axes: LayoutAxes::new(LTR, TTB),
align: LayoutAlign::new(Start, Start),
root: true,
},
).await
layout(&tree, LayoutContext {
loader: &self.loader,
scope: &self.std,
style: &self.style,
base: self.style.page.size.unpadded(margins),
spaces: vec![LayoutSpace {
size: self.style.page.size,
padding: margins,
expansion: LayoutExpansion::new(true, true),
}],
repeat: true,
axes: LayoutAxes::new(LTR, TTB),
align: LayoutAlign::new(Start, Start),
root: true,
})
.await
}
/// Process source code directly into a collection of layouts.
@ -177,10 +175,7 @@ pub struct Feedback {
impl Feedback {
/// Create a new feedback instance without errors and decos.
pub fn new() -> Self {
Self {
diagnostics: vec![],
decorations: vec![],
}
Self { diagnostics: vec![], decorations: vec![] }
}
/// Merged two feedbacks into one.

View File

@ -1,12 +1,16 @@
use crate::length::ScaleLength;
use super::*;
use crate::length::ScaleLength;
/// `box`: Layouts its contents into a box.
///
/// # Keyword arguments
/// - `width`: The width of the box (length of relative to parent's width).
/// - `height`: The height of the box (length of relative to parent's height).
pub async fn boxed(_: Span, mut args: TableValue, mut ctx: LayoutContext<'_>) -> Pass<Value> {
pub async fn boxed(
_: Span,
mut args: TableValue,
mut ctx: LayoutContext<'_>,
) -> Pass<Value> {
let mut f = Feedback::new();
let content = args.take::<SyntaxTree>().unwrap_or(SyntaxTree::new());

View File

@ -1,5 +1,5 @@
use crate::color::RgbaColor;
use super::*;
use crate::color::RgbaColor;
/// `rgb`: Create an RGB(A) color.
pub async fn rgb(span: Span, mut args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
@ -11,20 +11,17 @@ pub async fn rgb(span: Span, mut args: TableValue, _: LayoutContext<'_>) -> Pass
let a = args.take::<Spanned<f64>>();
let mut clamp = |component: Option<Spanned<f64>>, default| {
component.map(|c| {
if c.v < 0.0 || c.v > 255.0 {
error!(@f, c.span, "should be between 0 and 255")
}
c.v.min(255.0).max(0.0).round() as u8
}).unwrap_or(default)
component
.map(|c| {
if c.v < 0.0 || c.v > 255.0 {
error!(@f, c.span, "should be between 0 and 255")
}
c.v.min(255.0).max(0.0).round() as u8
})
.unwrap_or(default)
};
let color = RgbaColor::new(
clamp(r, 0),
clamp(g, 0),
clamp(b, 0),
clamp(a, 255),
);
let color = RgbaColor::new(clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255));
args.unexpected(&mut f);
Pass::new(Value::Color(color), f)

View File

@ -1,7 +1,7 @@
use fontdock::{FontStyle, FontWeight, FontWidth};
use crate::length::ScaleLength;
use super::*;
use crate::length::ScaleLength;
/// `font`: Configure the font.
///
@ -35,7 +35,8 @@ pub async fn font(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass
}
}
let list: Vec<_> = args.take_all_num_vals::<StringLike>()
let list: Vec<_> = args
.take_all_num_vals::<StringLike>()
.map(|s| s.to_lowercase())
.collect();
@ -57,7 +58,8 @@ pub async fn font(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass
}
for (class, mut table) in args.take_all_str::<TableValue>() {
let fallback = table.take_all_num_vals::<StringLike>()
let fallback = table
.take_all_num_vals::<StringLike>()
.map(|s| s.to_lowercase())
.collect();

View File

@ -1,6 +1,6 @@
use super::*;
use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass};
use super::*;
/// `page`: Configure pages.
///

View File

@ -1,6 +1,6 @@
use super::*;
use crate::layout::SpacingKind;
use crate::length::ScaleLength;
use super::*;
/// `h`: Add horizontal spacing.
///

View File

@ -1,4 +1,5 @@
/// Unwrap the option if it is `Some(T)` or evaluate `$or` if it is `None`.
#[allow(unused)]
macro_rules! try_or {
($option:expr, $or:expr $(,)?) => {
match $option {

View File

@ -39,19 +39,21 @@ pub enum PaperClass {
impl PaperClass {
/// The default margins for this page class.
pub fn default_margins(self) -> Value4<ScaleLength> {
let values = |l, t, r, b| Value4::new(
ScaleLength::Scaled(l),
ScaleLength::Scaled(t),
ScaleLength::Scaled(r),
ScaleLength::Scaled(b),
);
let values = |l, t, r, b| {
Value4::new(
ScaleLength::Scaled(l),
ScaleLength::Scaled(t),
ScaleLength::Scaled(r),
ScaleLength::Scaled(b),
)
};
match self {
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::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),
Self::Book => values(0.1200, 0.0852, 0.1500, 0.0965),
}
}
}

View File

@ -1,12 +1,12 @@
//! A prelude for building custom functions.
pub use super::*;
pub use crate::compute::value::*;
pub use crate::layout::prelude::*;
pub use crate::layout::Commands;
pub use crate::layout::Command::{self, *};
pub use crate::layout::Commands;
pub use crate::style::*;
pub use crate::syntax::parsing::parse;
pub use crate::syntax::span::{Pos, Span, SpanVec, Spanned};
pub use crate::syntax::tree::*;
pub use crate::{Pass, Feedback};
pub use super::*;
pub use crate::{Feedback, Pass};

View File

@ -1,8 +1,6 @@
//! Styles for text and pages.
use fontdock::{
fallback, FallbackTree, FontStyle, FontVariant, FontWeight, FontWidth,
};
use fontdock::{fallback, FallbackTree, FontStyle, FontVariant, FontWeight, FontWidth};
use crate::geom::{Margins, Size, Value4};
use crate::length::{Length, ScaleLength};

View File

@ -37,9 +37,9 @@ impl Debug for Ident {
#[cfg(test)]
mod tests {
use std::fmt::Debug;
use crate::prelude::*;
use super::span;
use crate::prelude::*;
use std::fmt::Debug;
/// Assert that expected and found are equal, printing both and panicking
/// and the source of their test case if they aren't.

View File

@ -2,16 +2,14 @@
use std::str::FromStr;
use crate::{Feedback, Pass};
use crate::color::RgbaColor;
use crate::compute::table::SpannedEntry;
use super::decoration::Decoration;
use super::span::{Pos, Span, Spanned};
use super::tokens::{is_newline_char, Token, TokenMode, Tokens};
use super::tree::{
CallExpr, Expr, SyntaxNode, SyntaxTree, TableExpr, Code,
};
use super::tree::{CallExpr, Code, Expr, SyntaxNode, SyntaxTree, TableExpr};
use super::Ident;
use crate::color::RgbaColor;
use crate::compute::table::SpannedEntry;
use crate::{Feedback, Pass};
/// Parse a string of source code.
pub fn parse(src: &str) -> Pass<SyntaxTree> {
@ -106,9 +104,7 @@ impl Parser<'_> {
self.with_span(SyntaxNode::Code(Code { lang, lines, block }))
}
Token::Text(text) => {
self.with_span(SyntaxNode::Text(text.to_string()))
}
Token::Text(text) => self.with_span(SyntaxNode::Text(text.to_string())),
Token::UnicodeEscape { sequence, terminated } => {
if !terminated {
@ -222,7 +218,10 @@ impl Parser<'_> {
let mut table = TableExpr::new();
let mut comma_and_keyless = true;
while { self.skip_white(); !self.eof() } {
while {
self.skip_white();
!self.eof()
} {
let (key, val) = if let Some(ident) = self.parse_ident() {
self.skip_white();
@ -230,11 +229,12 @@ impl Parser<'_> {
Some(Token::Equals) => {
self.eat();
self.skip_white();
(Some(ident), try_or!(self.parse_expr(), {
if let Some(value) = self.parse_expr() {
(Some(ident), value)
} else {
self.expected("value");
continue;
}))
}
}
Some(Token::LeftParen) => {
@ -242,26 +242,30 @@ impl Parser<'_> {
(None, call.map(Expr::Call))
}
_ => (None, ident.map(Expr::Ident))
_ => (None, ident.map(Expr::Ident)),
}
} else if let Some(value) = self.parse_expr() {
(None, value)
} else {
(None, try_or!(self.parse_expr(), {
self.expected("value");
continue;
}))
self.expected("value");
continue;
};
let behind = val.span.end;
if let Some(key) = key {
comma_and_keyless = false;
table.insert(key.v.0, SpannedEntry::new(key.span, val));
self.feedback.decorations
self.feedback
.decorations
.push(Spanned::new(Decoration::TableKey, key.span));
} else {
table.push(SpannedEntry::val(val));
}
if { self.skip_white(); self.eof() } {
if {
self.skip_white();
self.eof()
} {
break;
}
@ -274,6 +278,8 @@ impl Parser<'_> {
}
}
type Binop = fn(Box<Spanned<Expr>>, Box<Spanned<Expr>>) -> Expr;
// Expressions and values.
impl Parser<'_> {
fn parse_expr(&mut self) -> Option<Spanned<Expr>> {
@ -297,9 +303,7 @@ impl Parser<'_> {
&mut self,
operand_name: &str,
mut parse_operand: impl FnMut(&mut Self) -> Option<Spanned<Expr>>,
mut parse_op: impl FnMut(Token) -> Option<
fn(Box<Spanned<Expr>>, Box<Spanned<Expr>>) -> Expr
>,
mut parse_op: impl FnMut(Token) -> Option<Binop>,
) -> Option<Spanned<Expr>> {
let mut left = parse_operand(self)?;
@ -388,9 +392,7 @@ impl Parser<'_> {
let span = self.end_group();
let expr = if coercable {
table.into_values()
.next()
.expect("table is coercable").val.v
table.into_values().next().expect("table is coercable").val.v
} else {
Expr::Table(table)
};
@ -478,8 +480,7 @@ impl<'s> Parser<'s> {
fn end_group(&mut self) -> Span {
let peeked = self.peek();
let (start, end_token) = self.delimiters.pop()
.expect("group was not started");
let (start, end_token) = self.delimiters.pop().expect("group was not started");
if end_token != Token::Chain && peeked != None {
self.delimiters.push((start, end_token));
@ -528,11 +529,7 @@ impl<'s> Parser<'s> {
}
fn check_eat(&mut self, token: Token<'_>) -> Option<Spanned<Token<'s>>> {
if self.check(token) {
self.eat()
} else {
None
}
if self.check(token) { self.eat() } else { None }
}
/// Checks if the next token is of some kind
@ -589,8 +586,7 @@ impl Group {
fn is_delimiter(token: Token<'_>) -> bool {
matches!(
token,
Token::RightParen | Token::RightBracket
| Token::RightBrace | Token::Chain
Token::RightParen | Token::RightBracket | Token::RightBrace | Token::Chain
)
}
@ -654,7 +650,10 @@ fn unescape_string(string: &str) -> String {
}
Some('n') => out.push('\n'),
Some('t') => out.push('\t'),
Some(c) => { out.push('\\'); out.push(c); }
Some(c) => {
out.push('\\');
out.push(c);
}
None => out.push('\\'),
}
} else {
@ -784,22 +783,20 @@ fn split_lines(text: &str) -> Vec<String> {
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use crate::syntax::tests::*;
use crate::length::Length;
use super::*;
use crate::length::Length;
use crate::syntax::tests::*;
use Decoration::*;
// ----------------------- Construct Syntax Nodes ----------------------- //
use SyntaxNode::{
Spacing as S,
Linebreak as L,
Parbreak as P,
ToggleItalic as I,
ToggleBolder as B,
Linebreak as L, Parbreak as P, Spacing as S, ToggleBolder as B, ToggleItalic as I,
};
fn T(text: &str) -> SyntaxNode { SyntaxNode::Text(text.to_string()) }
fn T(text: &str) -> SyntaxNode {
SyntaxNode::Text(text.to_string())
}
macro_rules! R {
($($line:expr),* $(,)?) => {
@ -832,10 +829,14 @@ mod tests {
// ------------------------ Construct Expressions ----------------------- //
use Expr::{Bool, Number as Num, Length as Len, Color};
use Expr::{Bool, Color, Length as Len, Number as Num};
fn Id(ident: &str) -> Expr { Expr::Ident(Ident(ident.to_string())) }
fn Str(string: &str) -> Expr { Expr::Str(string.to_string()) }
fn Id(ident: &str) -> Expr {
Expr::Ident(Ident(ident.to_string()))
}
fn Str(string: &str) -> Expr {
Expr::Str(string.to_string())
}
macro_rules! Table {
(@table=$table:expr,) => {};
@ -937,6 +938,7 @@ mod tests {
// -------------------------------- Tests ------------------------------- //
#[test]
#[rustfmt::skip]
fn test_unescape_strings() {
fn test(string: &str, expected: &str) {
assert_eq!(unescape_string(string), expected.to_string());
@ -957,6 +959,7 @@ mod tests {
}
#[test]
#[rustfmt::skip]
fn test_unescape_raws() {
fn test(raw: &str, expected: Vec<&str>) {
assert_eq!(unescape_raw(raw), expected);
@ -974,6 +977,7 @@ mod tests {
}
#[test]
#[rustfmt::skip]
fn test_unescape_code() {
fn test(raw: &str, expected: Vec<&str>) {
assert_eq!(unescape_code(raw), expected);
@ -1060,7 +1064,7 @@ mod tests {
s(0,1, 0,1, "expected closing bracket"));
// No name.
e!("[]" => s(0,1, 0,1, "expected function name"));
e!("[]" => s(0,1, 0,1, "expected function name"));
e!("[\"]" => s(0,1, 0,3, "expected function name, found string"),
s(0,3, 0,3, "expected closing bracket"));
@ -1077,8 +1081,8 @@ mod tests {
fn test_parse_chaining() {
// Things the parser has to make sense of
t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Table![Num(5.0), Num(2.1)], Tree![F!("you")]));
t!("[box >>][Hi]" => F!("box"; Tree![T("Hi")]));
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
t!("[box >>][Hi]" => F!("box"; Tree![T("Hi")]));
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi")))
]));
t!("[bold: 400, >> emph >> sub: 1cm]" => F!("bold"; Num(400.0), Tree![
@ -1110,7 +1114,7 @@ mod tests {
#[test]
fn test_parse_function_bodies() {
t!("[val: 1][*Hi*]" => F!("val"; Num(1.0), Tree![B, T("Hi"), B]));
e!(" [val][ */ ]" => s(0,8, 0,10, "unexpected end of block comment"));
e!(" [val][ */ ]" => s(0,8, 0,10, "unexpected end of block comment"));
// Raw in body.
t!("[val][`Hi]`" => F!("val"; Tree![R!["Hi]"]]));
@ -1148,10 +1152,10 @@ mod tests {
v!("\"a\n[]\\\"string\"" => Str("a\n[]\"string"));
// Content.
v!("{_hi_}" => Tree![I, T("hi"), I]);
e!("[val: {_hi_}]" => );
v!("[hi]" => Tree![F!["hi"]]);
e!("[val: [hi]]" => );
v!("{_hi_}" => Tree![I, T("hi"), I]);
e!("[val: {_hi_}]" => );
v!("[hi]" => Tree![F!("hi")]);
e!("[val: [hi]]" => );
// Healed colors.
v!("#12345" => Color(RgbaColor::new_healed(0, 0, 0, 0xff)));
@ -1232,8 +1236,7 @@ mod tests {
// Spanned with spacing around keyword arguments.
ts!("[val: \n hi \n = /* //\n */ \"s\n\"]" => s(0,0, 4,2, F!(
s(0,1, 0,4, "val");
s(1,1, 1,3, "hi") => s(3,4, 4,1, Str("s\n"))
s(0,1, 0,4, "val"); s(1,1, 1,3, "hi") => s(3,4, 4,1, Str("s\n"))
)));
e!("[val: \n hi \n = /* //\n */ \"s\n\"]" => );
}

View File

@ -4,8 +4,8 @@ use std::iter::Peekable;
use std::str::Chars;
use unicode_xid::UnicodeXID;
use crate::length::Length;
use super::span::{Pos, Span, Spanned};
use crate::length::Length;
use Token::*;
use TokenMode::*;
@ -224,7 +224,10 @@ impl<'s> Iterator for Tokens<'s> {
// Comments.
'/' if self.peek() == Some('/') => self.read_line_comment(),
'/' if self.peek() == Some('*') => self.read_block_comment(),
'*' if self.peek() == Some('/') => { self.eat(); Invalid("*/") }
'*' if self.peek() == Some('/') => {
self.eat();
Invalid("*/")
}
// Whitespace.
c if c.is_whitespace() => self.read_whitespace(start),
@ -241,8 +244,7 @@ impl<'s> Iterator for Tokens<'s> {
':' if self.mode == Header => Colon,
',' if self.mode == Header => Comma,
'=' if self.mode == Header => Equals,
'>' if self.mode == Header && self.peek() == Some('>') =>
self.read_chain(),
'>' if self.mode == Header && self.peek() == Some('>') => self.read_chain(),
// Expression operators.
'+' if self.mode == Header => Plus,
@ -270,20 +272,22 @@ impl<'s> Iterator for Tokens<'s> {
c => {
let body = self.mode == Body;
let start_offset = -(c.len_utf8() as isize);
let mut last_was_e = false;
let text = self.read_string_until(|n| {
let (text, _) = self.read_string_until(false, start_offset, 0, |n| {
let val = match n {
c if c.is_whitespace() => true,
'[' | ']' | '{' | '}' | '/' | '*' => true,
'[' | ']' | '{' | '}' | '/' | '*' => true,
'\\' | '_' | '`' if body => true,
':' | '=' | ',' | '"' | '(' | ')' if !body => true,
'+' | '-' if !body && !last_was_e => true,
':' | '=' | ',' | '"' | '(' | ')' if !body => true,
'+' | '-' if !body && !last_was_e => true,
_ => false,
};
last_was_e = n == 'e' || n == 'E';
val
}, false, -(c.len_utf8() as isize), 0).0;
});
if self.mode == Header {
self.read_expr(text)
@ -302,35 +306,41 @@ impl<'s> Iterator for Tokens<'s> {
impl<'s> Tokens<'s> {
fn read_line_comment(&mut self) -> Token<'s> {
LineComment(self.read_string_until(is_newline_char, false, 1, 0).0)
self.eat();
LineComment(self.read_string_until(false, 0, 0, is_newline_char).0)
}
fn read_block_comment(&mut self) -> Token<'s> {
enum Last { Slash, Star, Other }
self.eat();
enum Last {
Slash,
Star,
Other,
}
let mut depth = 0;
let mut last = Last::Other;
// Find the first `*/` that does not correspond to a nested `/*`.
// Remove the last two bytes to obtain the raw inner text without `*/`.
BlockComment(self.read_string_until(|n| {
match n {
self.eat();
let (content, _) = self.read_string_until(true, 0, -2, |c| {
match c {
'/' => match last {
Last::Star if depth == 0 => return true,
Last::Star => depth -= 1,
_ => last = Last::Slash
}
_ => last = Last::Slash,
},
'*' => match last {
Last::Slash => depth += 1,
_ => last = Last::Star,
}
},
_ => last = Last::Other,
}
false
}, true, 0, -2).0)
});
BlockComment(content)
}
fn read_chain(&mut self) -> Token<'s> {
@ -339,7 +349,7 @@ impl<'s> Tokens<'s> {
}
fn read_whitespace(&mut self, start: Pos) -> Token<'s> {
self.read_string_until(|n| !n.is_whitespace(), false, 0, 0);
self.read_string_until(false, 0, 0, |n| !n.is_whitespace());
let end = self.pos();
Space(end.line - start.line)
@ -358,11 +368,11 @@ impl<'s> Tokens<'s> {
// Reads the lang tag (until newline or whitespace).
let start = self.pos();
let lang = self.read_string_until(
|c| c == '`' || c.is_whitespace() || is_newline_char(c),
false, 0, 0,
).0;
let (lang, _) = self.read_string_until(false, 0, 0, |c| {
c == '`' || c.is_whitespace() || is_newline_char(c)
});
let end = self.pos();
let lang = if !lang.is_empty() {
Some(Spanned::new(lang, Span::new(start, end)))
} else {
@ -405,25 +415,25 @@ impl<'s> Tokens<'s> {
Code {
lang,
raw: &self.src[start..end],
terminated
raw: &self.src[start .. end],
terminated,
}
} else {
Raw { raw, terminated }
}
}
fn read_until_unescaped(&mut self, c: char) -> (&'s str, bool) {
fn read_until_unescaped(&mut self, end: char) -> (&'s str, bool) {
let mut escaped = false;
self.read_string_until(|n| {
match n {
n if n == c && !escaped => return true,
self.read_string_until(true, 0, -1, |c| {
match c {
c if c == end && !escaped => return true,
'\\' => escaped = !escaped,
_ => escaped = false,
}
false
}, true, 0, -1)
})
}
fn read_escaped(&mut self) -> Token<'s> {
@ -439,10 +449,8 @@ impl<'s> Tokens<'s> {
self.eat();
if self.peek() == Some('{') {
self.eat();
let sequence = self.read_string_until(
|c| !c.is_ascii_hexdigit(),
false, 0, 0,
).0;
let (sequence, _) =
self.read_string_until(false, 0, 0, |c| !c.is_ascii_hexdigit());
let terminated = self.peek() == Some('}');
if terminated {
@ -457,7 +465,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("\\"),
@ -468,10 +476,7 @@ impl<'s> Tokens<'s> {
fn read_hex(&mut self) -> Token<'s> {
// This will parse more than the permissable 0-9, a-f, A-F character
// ranges to provide nicer error messages later.
Hex(self.read_string_until(
|n| !n.is_ascii_alphanumeric(),
false, 0, 0
).0)
Hex(self.read_string_until(false, 0, 0, |n| !n.is_ascii_alphanumeric()).0)
}
fn read_expr(&mut self, text: &'s str) -> Token<'s> {
@ -497,10 +502,10 @@ impl<'s> Tokens<'s> {
/// after the match depending on `eat_match`.
fn read_string_until(
&mut self,
mut f: impl FnMut(char) -> bool,
eat_match: bool,
offset_start: isize,
offset_end: isize,
mut f: impl FnMut(char) -> bool,
) -> (&'s str, bool) {
let start = ((self.index() as isize) + offset_start) as usize;
let mut matched = false;
@ -522,7 +527,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> {
@ -546,7 +551,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
}
@ -556,7 +561,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,
@ -588,35 +593,33 @@ pub fn is_identifier(string: &str) -> bool {
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::super::span::Spanned;
use super::*;
use crate::length::Length;
use crate::syntax::tests::*;
use super::*;
use super::super::span::Spanned;
use Token::{
Space as S,
LineComment as LC, BlockComment as BC,
LeftBracket as L, RightBracket as R,
LeftParen as LP, RightParen as RP,
LeftBrace as LB, RightBrace as RB,
Chain,
Ident as Id,
Bool,
Number as Num,
Length as Len,
Hex,
Plus,
Hyphen as Min,
Slash,
Star,
Text as T,
BlockComment as BC, Bool, Chain, Hex, Hyphen as Min, Ident as Id,
LeftBrace as LB, LeftBracket as L, LeftParen as LP, Length as Len,
LineComment as LC, Number as Num, Plus, RightBrace as RB, RightBracket as R,
RightParen as RP, Slash, Space as S, Star, Text as T,
};
fn Str(string: &str, terminated: bool) -> Token { Token::Str { string, terminated } }
fn Raw(raw: &str, terminated: bool) -> Token { Token::Raw { raw, terminated } }
fn Code<'a>(lang: Option<&'a str>, raw: &'a str, terminated: bool) -> Token<'a> {
Token::Code { lang: lang.map(Spanned::zero), raw, terminated }
fn Str(string: &str, terminated: bool) -> Token {
Token::Str { string, terminated }
}
fn Raw(raw: &str, terminated: bool) -> Token {
Token::Raw { raw, terminated }
}
fn Code<'a>(lang: Option<&'a str>, raw: &'a str, terminated: bool) -> Token<'a> {
Token::Code {
lang: lang.map(Spanned::zero),
raw,
terminated,
}
}
fn UE(sequence: &str, terminated: bool) -> Token {
Token::UnicodeEscape { sequence, terminated }
}
fn UE(sequence: &str, terminated: bool) -> Token { Token::UnicodeEscape { sequence, terminated } }
macro_rules! t { ($($tts:tt)*) => {test!(@spans=false, $($tts)*)} }
macro_rules! ts { ($($tts:tt)*) => {test!(@spans=true, $($tts)*)} }

View File

@ -2,15 +2,15 @@
use std::fmt::{self, Debug, Formatter};
use super::decoration::Decoration;
use super::span::{SpanVec, Spanned};
use super::Ident;
use crate::color::RgbaColor;
use crate::compute::table::{SpannedEntry, Table};
use crate::compute::value::{TableValue, Value};
use crate::layout::LayoutContext;
use crate::length::Length;
use crate::{DynFuture, Feedback};
use super::decoration::Decoration;
use super::span::{Spanned, SpanVec};
use super::Ident;
/// A collection of nodes which form a tree together with the nodes' children.
pub type SyntaxTree = SpanVec<SyntaxNode>;
@ -96,11 +96,7 @@ impl Expr {
}
/// Evaluate the expression to a value.
pub async fn eval(
&self,
ctx: &LayoutContext<'_>,
f: &mut Feedback,
) -> Value {
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
use Expr::*;
match self {
Ident(i) => Value::Ident(i.clone()),

View File

@ -98,8 +98,10 @@ fn test(
" {:?}: {}:{}:{} - {}:{}: {}",
diagnostic.v.level,
path.display(),
span.start.line + 1, span.start.column + 1,
span.end.line + 1, span.end.column + 1,
span.start.line + 1,
span.start.column + 1,
span.end.line + 1,
span.end.column + 1,
diagnostic.v.message,
);
}
@ -137,28 +139,23 @@ impl TestFilter {
if self.perfect {
self.filter.iter().any(|p| name == p)
} else {
self.filter.is_empty()
|| self.filter.iter().any(|p| name.contains(p))
self.filter.is_empty() || self.filter.iter().any(|p| name.contains(p))
}
}
}
fn render(
layouts: &MultiLayout,
loader: &SharedFontLoader,
scale: f64,
) -> DrawTarget {
fn render(layouts: &MultiLayout, loader: &SharedFontLoader, scale: f64) -> DrawTarget {
let pad = scale * 10.0;
let width = 2.0 * pad + layouts.iter()
.map(|l| scale * l.size.x)
.max_by(|a, b| a.partial_cmp(&b).unwrap())
.unwrap()
.round();
let width = 2.0 * pad
+ layouts
.iter()
.map(|l| scale * l.size.x)
.max_by(|a, b| a.partial_cmp(&b).unwrap())
.unwrap()
.round();
let height = pad + layouts.iter()
.map(|l| scale * l.size.y + pad)
.sum::<f64>()
.round();
let height =
pad + layouts.iter().map(|l| scale * l.size.y + pad).sum::<f64>().round();
let mut surface = DrawTarget::new(width as i32, height as i32);
surface.clear(BLACK);