mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Move, rename and switch some things (boring) 🚚
- Problems -> Diagnostics - Position -> Pos - offset_spans -> Offset trait - Size -> Length (and some more size types renamed) - Paper into its own module - scope::Parser -> parsing::CallParser - Create `Decorations` alias - Remove lots of double newlines - Switch from f32 to f64
This commit is contained in:
parent
f34ba3dcda
commit
bbcdeb128c
@ -8,7 +8,6 @@ use typstc::{Typesetter, DebugErrorProvider};
|
|||||||
use typstc::toddle::query::fs::EagerFsProvider;
|
use typstc::toddle::query::fs::EagerFsProvider;
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = run() {
|
if let Err(err) = run() {
|
||||||
eprintln!("error: {}", err);
|
eprintln!("error: {}", err);
|
||||||
|
91
src/diagnostic.rs
Normal file
91
src/diagnostic.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//! Diagnostics (errors / warnings) in 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.
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use crate::syntax::span::SpanVec;
|
||||||
|
|
||||||
|
/// A list of spanned diagnostics.
|
||||||
|
pub type Diagnostics = SpanVec<Diagnostic>;
|
||||||
|
|
||||||
|
/// A diagnostic that arose in parsing or layouting.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
|
||||||
|
pub struct Diagnostic {
|
||||||
|
/// How severe / important the diagnostic is.
|
||||||
|
pub level: Level,
|
||||||
|
/// A message describing the diagnostic.
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How severe / important a diagnostic is.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum Level {
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic {
|
||||||
|
/// Create a new diagnostic from message and level.
|
||||||
|
pub fn new(message: impl Into<String>, level: Level) -> Self {
|
||||||
|
Self { message: message.into(), level }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a diagnostic with `Error` level.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use typstc::error;
|
||||||
|
/// # use typstc::syntax::span::Span;
|
||||||
|
/// # use typstc::Feedback;
|
||||||
|
/// # let span = Span::ZERO;
|
||||||
|
/// # let mut feedback = Feedback::new();
|
||||||
|
/// # let name = "";
|
||||||
|
/// // Create formatted error values.
|
||||||
|
/// let error = error!("expected {}", name);
|
||||||
|
///
|
||||||
|
/// // Create spanned errors.
|
||||||
|
/// let spanned = error!(span, "there is an error here");
|
||||||
|
///
|
||||||
|
/// // Create an error and directly add it to existing feedback.
|
||||||
|
/// error!(@feedback, span, "oh no!");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error {
|
||||||
|
($($tts:tt)*) => {
|
||||||
|
$crate::__impl_diagnostic!($crate::diagnostic::Level::Error; $($tts)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a diagnostic with `Warning` level.
|
||||||
|
///
|
||||||
|
/// This works exactly like `error!`. See its documentation for more
|
||||||
|
/// information.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warning {
|
||||||
|
($($tts:tt)*) => {
|
||||||
|
$crate::__impl_diagnostic!($crate::diagnostic::Level::Warning; $($tts)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backs the `error!` and `warning!` macros.
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! __impl_diagnostic {
|
||||||
|
($level:expr; @$feedback:expr, $($tts:tt)*) => {
|
||||||
|
$feedback.diagnostics.push($crate::__impl_diagnostic!($level; $($tts)*));
|
||||||
|
};
|
||||||
|
|
||||||
|
($level:expr; $fmt:literal $($tts:tt)*) => {
|
||||||
|
$crate::diagnostic::Diagnostic::new(format!($fmt $($tts)*), $level)
|
||||||
|
};
|
||||||
|
|
||||||
|
($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => {
|
||||||
|
$crate::syntax::span::Spanned::new(
|
||||||
|
$crate::__impl_diagnostic!($level; $fmt $($tts)*),
|
||||||
|
$span,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
@ -23,8 +23,7 @@ use toddle::tables::{
|
|||||||
|
|
||||||
use crate::GlobalFontLoader;
|
use crate::GlobalFontLoader;
|
||||||
use crate::layout::{MultiLayout, Layout, LayoutAction};
|
use crate::layout::{MultiLayout, Layout, LayoutAction};
|
||||||
use crate::size::Size;
|
use crate::length::Length;
|
||||||
|
|
||||||
|
|
||||||
/// Export a layouted list of boxes. The same font loader as used for
|
/// 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
|
/// layouting needs to be passed in here since the layout only contains
|
||||||
@ -128,8 +127,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
let rect = Rect::new(
|
let rect = Rect::new(
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
page.dimensions.x.to_pt(),
|
page.dimensions.x.to_pt() as f32,
|
||||||
page.dimensions.y.to_pt(),
|
page.dimensions.y.to_pt() as f32,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.writer.write_obj(
|
self.writer.write_obj(
|
||||||
@ -167,14 +166,14 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
|
|
||||||
LayoutAction::SetFont(id, size) => {
|
LayoutAction::SetFont(id, size) => {
|
||||||
active_font = (self.font_remap[id], size.to_pt());
|
active_font = (self.font_remap[id], size.to_pt());
|
||||||
text.tf(active_font.0 as u32 + 1, size.to_pt());
|
text.tf(active_font.0 as u32 + 1, size.to_pt() as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutAction::WriteText(string) => {
|
LayoutAction::WriteText(string) => {
|
||||||
if let Some(pos) = next_pos.take() {
|
if let Some(pos) = next_pos.take() {
|
||||||
let x = pos.x.to_pt();
|
let x = pos.x.to_pt();
|
||||||
let y = (page.dimensions.y - pos.y - Size::pt(active_font.1)).to_pt();
|
let y = (page.dimensions.y - pos.y - Length::pt(active_font.1)).to_pt();
|
||||||
text.tm(1.0, 0.0, 0.0, 1.0, x, y);
|
text.tm(1.0, 0.0, 0.0, 1.0, x as f32, y as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
text.tj(self.fonts[active_font.0].encode_text(&string)?);
|
text.tj(self.fonts[active_font.0].encode_text(&string)?);
|
||||||
@ -219,8 +218,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
// Extract information from the head and hmtx tables.
|
// Extract information from the head and hmtx tables.
|
||||||
let head = font.read_table::<Header>()?;
|
let head = font.read_table::<Header>()?;
|
||||||
|
|
||||||
let font_unit_ratio = 1.0 / (head.units_per_em as f32);
|
let font_unit_ratio = 1.0 / (head.units_per_em as f64);
|
||||||
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
|
let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
|
||||||
let font_unit_to_glyph_unit = |fu| {
|
let font_unit_to_glyph_unit = |fu| {
|
||||||
let size = font_unit_to_size(fu);
|
let size = font_unit_to_size(fu);
|
||||||
(1000.0 * size.to_pt()).round() as GlyphUnit
|
(1000.0 * size.to_pt()).round() as GlyphUnit
|
||||||
@ -228,10 +227,10 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
|
|
||||||
let italic = head.mac_style.contains(MacStyleFlags::ITALIC);
|
let italic = head.mac_style.contains(MacStyleFlags::ITALIC);
|
||||||
let bounding_box = Rect::new(
|
let bounding_box = Rect::new(
|
||||||
font_unit_to_glyph_unit(head.x_min as f32),
|
font_unit_to_glyph_unit(head.x_min as f64),
|
||||||
font_unit_to_glyph_unit(head.y_min as f32),
|
font_unit_to_glyph_unit(head.y_min as f64),
|
||||||
font_unit_to_glyph_unit(head.x_max as f32),
|
font_unit_to_glyph_unit(head.x_max as f64),
|
||||||
font_unit_to_glyph_unit(head.y_max as f32),
|
font_unit_to_glyph_unit(head.y_max as f64),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transform the width into PDF units.
|
// Transform the width into PDF units.
|
||||||
@ -239,7 +238,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
.read_table::<HorizontalMetrics>()?
|
.read_table::<HorizontalMetrics>()?
|
||||||
.metrics
|
.metrics
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| font_unit_to_glyph_unit(m.advance_width as f32))
|
.map(|m| font_unit_to_glyph_unit(m.advance_width as f64))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Write the CID font referencing the font descriptor.
|
// Write the CID font referencing the font descriptor.
|
||||||
@ -274,12 +273,12 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
// Write the font descriptor (contains the global information about the font).
|
// Write the font descriptor (contains the global information about the font).
|
||||||
self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle)
|
self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle)
|
||||||
.font_bbox(bounding_box)
|
.font_bbox(bounding_box)
|
||||||
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32))
|
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f64))
|
||||||
.descent(font_unit_to_glyph_unit(os2.s_typo_descender as f32))
|
.descent(font_unit_to_glyph_unit(os2.s_typo_descender as f64))
|
||||||
.cap_height(font_unit_to_glyph_unit(
|
.cap_height(font_unit_to_glyph_unit(
|
||||||
os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f32,
|
os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f64,
|
||||||
))
|
))
|
||||||
.stem_v((10.0 + 0.244 * (os2.us_weight_class as f32 - 50.0)) as GlyphUnit)
|
.stem_v((10.0 + 0.244 * (os2.us_weight_class as f64 - 50.0)) as GlyphUnit)
|
||||||
.font_file_2(id + 4)
|
.font_file_2(id + 4)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ pub trait ParseFunc {
|
|||||||
///
|
///
|
||||||
/// parse(header, body, state, f) {
|
/// parse(header, body, state, f) {
|
||||||
/// let body = body!(opt: body, state, f);
|
/// let body = body!(opt: body, state, f);
|
||||||
/// let hidden = header.args.pos.get::<bool>(&mut f.problems)
|
/// let hidden = header.args.pos.get::<bool>(&mut f.diagnostics)
|
||||||
/// .or_missing(&mut f.problems, header.name.span, "hidden")
|
/// .or_missing(&mut f.diagnostics, header.name.span, "hidden")
|
||||||
/// .unwrap_or(false);
|
/// .unwrap_or(false);
|
||||||
///
|
///
|
||||||
/// HiderFunc { body: if hidden { None } else { body } }
|
/// HiderFunc { body: if hidden { None } else { body } }
|
||||||
|
@ -4,23 +4,22 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use serde::ser::{Serialize, Serializer, SerializeTuple};
|
use serde::ser::{Serialize, Serializer, SerializeTuple};
|
||||||
use toddle::query::FontIndex;
|
use toddle::query::FontIndex;
|
||||||
|
|
||||||
use crate::size::{Size, Size2D};
|
use crate::length::{Length, Size};
|
||||||
use super::Layout;
|
use super::Layout;
|
||||||
use self::LayoutAction::*;
|
use self::LayoutAction::*;
|
||||||
|
|
||||||
|
|
||||||
/// A layouting action, which is the basic building block layouts are composed
|
/// A layouting action, which is the basic building block layouts are composed
|
||||||
/// of.
|
/// of.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum LayoutAction {
|
pub enum LayoutAction {
|
||||||
/// Move to an absolute position.
|
/// Move to an absolute position.
|
||||||
MoveAbsolute(Size2D),
|
MoveAbsolute(Size),
|
||||||
/// Set the font given the index from the font loader and font size.
|
/// Set the font given the index from the font loader and font size.
|
||||||
SetFont(FontIndex, Size),
|
SetFont(FontIndex, Length),
|
||||||
/// Write text at the current position.
|
/// Write text at the current position.
|
||||||
WriteText(String),
|
WriteText(String),
|
||||||
/// Visualize a box for debugging purposes.
|
/// Visualize a box for debugging purposes.
|
||||||
DebugBox(Size2D),
|
DebugBox(Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for LayoutAction {
|
impl Serialize for LayoutAction {
|
||||||
@ -81,11 +80,11 @@ impl Debug for LayoutAction {
|
|||||||
/// position.
|
/// position.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LayoutActions {
|
pub struct LayoutActions {
|
||||||
origin: Size2D,
|
origin: Size,
|
||||||
actions: Vec<LayoutAction>,
|
actions: Vec<LayoutAction>,
|
||||||
active_font: (FontIndex, Size),
|
active_font: (FontIndex, Length),
|
||||||
next_pos: Option<Size2D>,
|
next_pos: Option<Size>,
|
||||||
next_font: Option<(FontIndex, Size)>,
|
next_font: Option<(FontIndex, Length)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutActions {
|
impl LayoutActions {
|
||||||
@ -93,8 +92,8 @@ impl LayoutActions {
|
|||||||
pub fn new() -> LayoutActions {
|
pub fn new() -> LayoutActions {
|
||||||
LayoutActions {
|
LayoutActions {
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
origin: Size2D::ZERO,
|
origin: Size::ZERO,
|
||||||
active_font: (FontIndex::MAX, Size::ZERO),
|
active_font: (FontIndex::MAX, Length::ZERO),
|
||||||
next_pos: None,
|
next_pos: None,
|
||||||
next_font: None,
|
next_font: None,
|
||||||
}
|
}
|
||||||
@ -126,7 +125,7 @@ impl LayoutActions {
|
|||||||
|
|
||||||
/// Add a layout at a position. All move actions inside the layout are
|
/// Add a layout at a position. All move actions inside the layout are
|
||||||
/// translated by the position.
|
/// translated by the position.
|
||||||
pub fn add_layout(&mut self, position: Size2D, layout: Layout) {
|
pub fn add_layout(&mut self, position: Size, layout: Layout) {
|
||||||
self.flush_position();
|
self.flush_position();
|
||||||
|
|
||||||
self.origin = position;
|
self.origin = position;
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
use super::stack::{StackLayouter, StackContext};
|
use super::stack::{StackLayouter, StackContext};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Performs the line layouting.
|
/// Performs the line layouting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LineLayouter {
|
pub struct LineLayouter {
|
||||||
@ -40,7 +39,7 @@ pub struct LineContext {
|
|||||||
/// extent of the layout.
|
/// extent of the layout.
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
/// The line spacing.
|
/// The line spacing.
|
||||||
pub line_spacing: Size,
|
pub line_spacing: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A line run is a sequence of boxes with the same alignment that are arranged
|
/// A line run is a sequence of boxes with the same alignment that are arranged
|
||||||
@ -49,10 +48,10 @@ pub struct LineContext {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct LineRun {
|
struct LineRun {
|
||||||
/// The so-far accumulated layouts in the line.
|
/// The so-far accumulated layouts in the line.
|
||||||
layouts: Vec<(Size, Layout)>,
|
layouts: Vec<(Length, Layout)>,
|
||||||
/// The width (primary size) and maximal height (secondary size) of the
|
/// The width (primary length) and maximal height (secondary length) of the
|
||||||
/// line.
|
/// line.
|
||||||
size: Size2D,
|
size: Size,
|
||||||
/// The alignment of all layouts in the line.
|
/// The alignment of all layouts in the line.
|
||||||
///
|
///
|
||||||
/// When a new run is created the alignment is yet to be determined. Once a
|
/// When a new run is created the alignment is yet to be determined. Once a
|
||||||
@ -61,7 +60,7 @@ struct LineRun {
|
|||||||
alignment: Option<LayoutAlignment>,
|
alignment: Option<LayoutAlignment>,
|
||||||
/// If another line run with different alignment already took up some space
|
/// 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.
|
/// of the line, this run has less space and how much is stored here.
|
||||||
usable: Option<Size>,
|
usable: Option<Length>,
|
||||||
/// A possibly cached soft spacing or spacing state.
|
/// A possibly cached soft spacing or spacing state.
|
||||||
last_spacing: LastSpacing,
|
last_spacing: LastSpacing,
|
||||||
}
|
}
|
||||||
@ -156,7 +155,7 @@ impl LineLayouter {
|
|||||||
///
|
///
|
||||||
/// This specifies how much more fits before a line break needs to be
|
/// This specifies how much more fits before a line break needs to be
|
||||||
/// issued.
|
/// issued.
|
||||||
fn usable(&self) -> Size2D {
|
fn usable(&self) -> Size {
|
||||||
// The base is the usable space per stack layouter.
|
// The base is the usable space per stack layouter.
|
||||||
let mut usable = self.stack.usable().generalized(self.ctx.axes);
|
let mut usable = self.stack.usable().generalized(self.ctx.axes);
|
||||||
|
|
||||||
@ -171,7 +170,7 @@ impl LineLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add spacing along the primary axis to the line.
|
/// Add spacing along the primary axis to the line.
|
||||||
pub fn add_primary_spacing(&mut self, mut spacing: Size, kind: SpacingKind) {
|
pub fn add_primary_spacing(&mut self, mut spacing: Length, kind: SpacingKind) {
|
||||||
match kind {
|
match kind {
|
||||||
// A hard space is simply an empty box.
|
// A hard space is simply an empty box.
|
||||||
SpacingKind::Hard => {
|
SpacingKind::Hard => {
|
||||||
@ -197,7 +196,7 @@ impl LineLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the line and add secondary spacing to the underlying stack.
|
/// Finish the line and add secondary spacing to the underlying stack.
|
||||||
pub fn add_secondary_spacing(&mut self, spacing: Size, kind: SpacingKind) {
|
pub fn add_secondary_spacing(&mut self, spacing: Length, kind: SpacingKind) {
|
||||||
self.finish_line_if_not_empty();
|
self.finish_line_if_not_empty();
|
||||||
self.stack.add_spacing(spacing, kind)
|
self.stack.add_spacing(spacing, kind)
|
||||||
}
|
}
|
||||||
@ -219,7 +218,7 @@ impl LineLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the line spacing.
|
/// Update the line spacing.
|
||||||
pub fn set_line_spacing(&mut self, line_spacing: Size) {
|
pub fn set_line_spacing(&mut self, line_spacing: Length) {
|
||||||
self.ctx.line_spacing = line_spacing;
|
self.ctx.line_spacing = line_spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +234,7 @@ impl LineLayouter {
|
|||||||
|
|
||||||
/// Whether the currently set line is empty.
|
/// Whether the currently set line is empty.
|
||||||
pub fn line_is_empty(&self) -> bool {
|
pub fn line_is_empty(&self) -> bool {
|
||||||
self.run.size == Size2D::ZERO && self.run.layouts.is_empty()
|
self.run.size == Size::ZERO && self.run.layouts.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the last line and compute the final list of boxes.
|
/// Finish the last line and compute the final list of boxes.
|
||||||
@ -265,7 +264,7 @@ impl LineLayouter {
|
|||||||
- layout.dimensions.primary(self.ctx.axes),
|
- layout.dimensions.primary(self.ctx.axes),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pos = Size2D::with_x(x);
|
let pos = Size::with_x(x);
|
||||||
actions.add_layout(pos, layout);
|
actions.add_layout(pos, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +292,7 @@ impl LineRun {
|
|||||||
fn new() -> LineRun {
|
fn new() -> LineRun {
|
||||||
LineRun {
|
LineRun {
|
||||||
layouts: vec![],
|
layouts: vec![],
|
||||||
size: Size2D::ZERO,
|
size: Size::ZERO,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
usable: None,
|
usable: None,
|
||||||
last_spacing: LastSpacing::Hard,
|
last_spacing: LastSpacing::Hard,
|
||||||
|
@ -5,7 +5,7 @@ use smallvec::SmallVec;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use toddle::query::FontIndex;
|
use toddle::query::FontIndex;
|
||||||
|
|
||||||
use crate::size::{Size, Size2D, SizeBox};
|
use crate::length::{Length, Size, Margins};
|
||||||
use self::prelude::*;
|
use self::prelude::*;
|
||||||
|
|
||||||
pub mod line;
|
pub mod line;
|
||||||
@ -27,7 +27,6 @@ pub mod prelude {
|
|||||||
pub use super::Alignment::{self, *};
|
pub use super::Alignment::{self, *};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A collection of layouts.
|
/// A collection of layouts.
|
||||||
pub type MultiLayout = Vec<Layout>;
|
pub type MultiLayout = Vec<Layout>;
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ pub type MultiLayout = Vec<Layout>;
|
|||||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
/// The size of the box.
|
/// The size of the box.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size,
|
||||||
/// How to align this layout in a parent container.
|
/// How to align this layout in a parent container.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub alignment: LayoutAlignment,
|
pub alignment: LayoutAlignment,
|
||||||
@ -66,9 +65,9 @@ pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
|
|||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct LayoutSpace {
|
pub struct LayoutSpace {
|
||||||
/// The maximum size of the box to layout in.
|
/// The maximum size of the box to layout in.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size,
|
||||||
/// Padding that should be respected on each side.
|
/// Padding that should be respected on each side.
|
||||||
pub padding: SizeBox,
|
pub padding: Margins,
|
||||||
/// Whether to expand the dimensions of the resulting layout to the full
|
/// Whether to expand the dimensions of the resulting layout to the full
|
||||||
/// dimensions of this space or to shrink them to fit the content.
|
/// dimensions of this space or to shrink them to fit the content.
|
||||||
pub expansion: LayoutExpansion,
|
pub expansion: LayoutExpansion,
|
||||||
@ -77,12 +76,12 @@ pub struct LayoutSpace {
|
|||||||
impl LayoutSpace {
|
impl LayoutSpace {
|
||||||
/// The offset from the origin to the start of content, that is,
|
/// The offset from the origin to the start of content, that is,
|
||||||
/// `(padding.left, padding.top)`.
|
/// `(padding.left, padding.top)`.
|
||||||
pub fn start(&self) -> Size2D {
|
pub fn start(&self) -> Size {
|
||||||
Size2D::new(self.padding.left, self.padding.top)
|
Size::new(self.padding.left, self.padding.top)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The actually usable area (dimensions minus padding).
|
/// The actually usable area (dimensions minus padding).
|
||||||
pub fn usable(&self) -> Size2D {
|
pub fn usable(&self) -> Size {
|
||||||
self.dimensions.unpadded(self.padding)
|
self.dimensions.unpadded(self.padding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ impl LayoutSpace {
|
|||||||
pub fn usable_space(&self) -> LayoutSpace {
|
pub fn usable_space(&self) -> LayoutSpace {
|
||||||
LayoutSpace {
|
LayoutSpace {
|
||||||
dimensions: self.usable(),
|
dimensions: self.usable(),
|
||||||
padding: SizeBox::ZERO,
|
padding: Margins::ZERO,
|
||||||
expansion: LayoutExpansion::new(false, false),
|
expansion: LayoutExpansion::new(false, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -369,17 +368,17 @@ enum LastSpacing {
|
|||||||
/// The last item was hard spacing.
|
/// The last item was hard spacing.
|
||||||
Hard,
|
Hard,
|
||||||
/// The last item was soft spacing with the given width and level.
|
/// The last item was soft spacing with the given width and level.
|
||||||
Soft(Size, u32),
|
Soft(Length, u32),
|
||||||
/// The last item was not spacing.
|
/// The last item was not spacing.
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LastSpacing {
|
impl LastSpacing {
|
||||||
/// The size of the soft space if this is a soft space or zero otherwise.
|
/// The length of the soft space if this is a soft space or zero otherwise.
|
||||||
fn soft_or_zero(self) -> Size {
|
fn soft_or_zero(self) -> Length {
|
||||||
match self {
|
match self {
|
||||||
LastSpacing::Soft(space, _) => space,
|
LastSpacing::Soft(space, _) => space,
|
||||||
_ => Size::ZERO,
|
_ => Length::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,13 @@ use toddle::query::FontStyle;
|
|||||||
use crate::{Pass, Feedback};
|
use crate::{Pass, Feedback};
|
||||||
use crate::GlobalFontLoader;
|
use crate::GlobalFontLoader;
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::size::{Size, Size2D};
|
use crate::length::{Length, Size};
|
||||||
use crate::syntax::{Model, SyntaxModel, Node, Decoration};
|
use crate::syntax::{Model, SyntaxModel, Node, Decoration};
|
||||||
use crate::syntax::span::{Span, Spanned};
|
use crate::syntax::span::{Span, Spanned};
|
||||||
use super::line::{LineLayouter, LineContext};
|
use super::line::{LineLayouter, LineContext};
|
||||||
use super::text::{layout_text, TextContext};
|
use super::text::{layout_text, TextContext};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Performs the model layouting.
|
/// Performs the model layouting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ModelLayouter<'a> {
|
pub struct ModelLayouter<'a> {
|
||||||
@ -36,7 +35,7 @@ pub struct LayoutContext<'a> {
|
|||||||
/// The style for pages and text.
|
/// The style for pages and text.
|
||||||
pub style: &'a LayoutStyle,
|
pub style: &'a LayoutStyle,
|
||||||
/// The base unpadded dimensions of this container (for relative sizing).
|
/// The base unpadded dimensions of this container (for relative sizing).
|
||||||
pub base: Size2D,
|
pub base: Size,
|
||||||
/// The spaces to layout in.
|
/// The spaces to layout in.
|
||||||
pub spaces: LayoutSpaces,
|
pub spaces: LayoutSpaces,
|
||||||
/// Whether to have repeated spaces or to use only the first and only once.
|
/// Whether to have repeated spaces or to use only the first and only once.
|
||||||
@ -76,7 +75,7 @@ pub enum Command<'a> {
|
|||||||
/// Add spacing of given [kind](super::SpacingKind) along the primary or
|
/// Add spacing of given [kind](super::SpacingKind) along the primary or
|
||||||
/// secondary axis. The spacing kind defines how the spacing interacts with
|
/// secondary axis. The spacing kind defines how the spacing interacts with
|
||||||
/// surrounding spacing.
|
/// surrounding spacing.
|
||||||
AddSpacing(Size, SpacingKind, GenericAxis),
|
AddSpacing(Length, SpacingKind, GenericAxis),
|
||||||
|
|
||||||
/// Start a new line.
|
/// Start a new line.
|
||||||
BreakLine,
|
BreakLine,
|
||||||
@ -159,7 +158,7 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
|
|
||||||
for Spanned { v: node, span } in &model.nodes {
|
for Spanned { v: node, span } in &model.nodes {
|
||||||
let decorate = |this: &mut ModelLayouter, deco| {
|
let decorate = |this: &mut ModelLayouter, deco| {
|
||||||
this.feedback.decos.push(Spanned::new(deco, *span));
|
this.feedback.decorations.push(Spanned::new(deco, *span));
|
||||||
};
|
};
|
||||||
|
|
||||||
match node {
|
match node {
|
||||||
|
@ -22,10 +22,9 @@
|
|||||||
//! sentence in the second box.
|
//! sentence in the second box.
|
||||||
|
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use crate::size::ValueBox;
|
use crate::length::Value4;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Performs the stack layouting.
|
/// Performs the stack layouting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StackLayouter {
|
pub struct StackLayouter {
|
||||||
@ -66,14 +65,14 @@ struct Space {
|
|||||||
/// The so-far accumulated layouts.
|
/// The so-far accumulated layouts.
|
||||||
layouts: Vec<(LayoutAxes, Layout)>,
|
layouts: Vec<(LayoutAxes, Layout)>,
|
||||||
/// The specialized size of this space.
|
/// The specialized size of this space.
|
||||||
size: Size2D,
|
size: Size,
|
||||||
/// The specialized remaining space.
|
/// The specialized remaining space.
|
||||||
usable: Size2D,
|
usable: Size,
|
||||||
/// The specialized extra-needed dimensions to affect the size at all.
|
/// The specialized extra-needed dimensions to affect the size at all.
|
||||||
extra: Size2D,
|
extra: Size,
|
||||||
/// The rulers of a space dictate which alignments for new boxes are still
|
/// The rulers of a space dictate which alignments for new boxes are still
|
||||||
/// allowed and which require a new space to be started.
|
/// allowed and which require a new space to be started.
|
||||||
rulers: ValueBox<Alignment>,
|
rulers: Value4<Alignment>,
|
||||||
/// The last added spacing if the last added thing was spacing.
|
/// The last added spacing if the last added thing was spacing.
|
||||||
last_spacing: LastSpacing,
|
last_spacing: LastSpacing,
|
||||||
}
|
}
|
||||||
@ -129,13 +128,13 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add secondary spacing to the stack.
|
/// Add secondary spacing to the stack.
|
||||||
pub fn add_spacing(&mut self, mut spacing: Size, kind: SpacingKind) {
|
pub fn add_spacing(&mut self, mut spacing: Length, kind: SpacingKind) {
|
||||||
match kind {
|
match kind {
|
||||||
// A hard space is simply an empty box.
|
// A hard space is simply an empty box.
|
||||||
SpacingKind::Hard => {
|
SpacingKind::Hard => {
|
||||||
// Reduce the spacing such that it definitely fits.
|
// Reduce the spacing such that it definitely fits.
|
||||||
spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
|
spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
|
||||||
let dimensions = Size2D::with_y(spacing);
|
let dimensions = Size::with_y(spacing);
|
||||||
|
|
||||||
self.update_metrics(dimensions);
|
self.update_metrics(dimensions);
|
||||||
self.space.layouts.push((self.ctx.axes, Layout {
|
self.space.layouts.push((self.ctx.axes, Layout {
|
||||||
@ -165,17 +164,17 @@ impl StackLayouter {
|
|||||||
|
|
||||||
/// Update the size metrics to reflect that a layout or spacing with the
|
/// Update the size metrics to reflect that a layout or spacing with the
|
||||||
/// given generalized dimensions has been added.
|
/// given generalized dimensions has been added.
|
||||||
fn update_metrics(&mut self, dimensions: Size2D) {
|
fn update_metrics(&mut self, dimensions: Size) {
|
||||||
let axes = self.ctx.axes;
|
let axes = self.ctx.axes;
|
||||||
|
|
||||||
let mut size = self.space.size.generalized(axes);
|
let mut size = self.space.size.generalized(axes);
|
||||||
let mut extra = self.space.extra.generalized(axes);
|
let mut extra = self.space.extra.generalized(axes);
|
||||||
|
|
||||||
size.x += (dimensions.x - extra.x).max(Size::ZERO);
|
size.x += (dimensions.x - extra.x).max(Length::ZERO);
|
||||||
size.y += (dimensions.y - extra.y).max(Size::ZERO);
|
size.y += (dimensions.y - extra.y).max(Length::ZERO);
|
||||||
|
|
||||||
extra.x.max_eq(dimensions.x);
|
extra.x.max_eq(dimensions.x);
|
||||||
extra.y = (extra.y - dimensions.y).max(Size::ZERO);
|
extra.y = (extra.y - dimensions.y).max(Length::ZERO);
|
||||||
|
|
||||||
self.space.size = size.specialized(axes);
|
self.space.size = size.specialized(axes);
|
||||||
self.space.extra = extra.specialized(axes);
|
self.space.extra = extra.specialized(axes);
|
||||||
@ -233,7 +232,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
/// Move to the first space that can fit the given dimensions or do nothing
|
/// Move to the first space that can fit the given dimensions or do nothing
|
||||||
/// if no space is capable of that.
|
/// if no space is capable of that.
|
||||||
pub fn skip_to_fitting_space(&mut self, dimensions: Size2D) {
|
pub fn skip_to_fitting_space(&mut self, dimensions: Size) {
|
||||||
let start = self.next_space();
|
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(dimensions) {
|
if space.usable().fits(dimensions) {
|
||||||
@ -251,7 +250,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
let mut spaces = smallvec![LayoutSpace {
|
let mut spaces = smallvec![LayoutSpace {
|
||||||
dimensions,
|
dimensions,
|
||||||
padding: SizeBox::ZERO,
|
padding: Margins::ZERO,
|
||||||
expansion: LayoutExpansion::new(false, false),
|
expansion: LayoutExpansion::new(false, false),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -263,15 +262,15 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The remaining usable size.
|
/// The remaining usable size.
|
||||||
pub fn usable(&self) -> Size2D {
|
pub fn usable(&self) -> Size {
|
||||||
self.space.usable
|
self.space.usable
|
||||||
- Size2D::with_y(self.space.last_spacing.soft_or_zero())
|
- Size::with_y(self.space.last_spacing.soft_or_zero())
|
||||||
.specialized(self.ctx.axes)
|
.specialized(self.ctx.axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the current layout space (not subspace) is empty.
|
/// Whether the current layout space (not subspace) is empty.
|
||||||
pub fn space_is_empty(&self) -> bool {
|
pub fn space_is_empty(&self) -> bool {
|
||||||
self.space.size == Size2D::ZERO && self.space.layouts.is_empty()
|
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 is the followup list.
|
||||||
@ -310,7 +309,7 @@ impl StackLayouter {
|
|||||||
let start = space.start();
|
let start = space.start();
|
||||||
|
|
||||||
let mut bounds = vec![];
|
let mut bounds = vec![];
|
||||||
let mut bound = SizeBox {
|
let mut bound = Margins {
|
||||||
left: start.x,
|
left: start.x,
|
||||||
top: start.y,
|
top: start.y,
|
||||||
right: start.x + self.space.size.x,
|
right: start.x + self.space.size.x,
|
||||||
@ -337,7 +336,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
// The `x` field stores the maximal primary extent in one axis-aligned
|
// The `x` field stores the maximal primary extent in one axis-aligned
|
||||||
// run, while the `y` fields stores the accumulated secondary extent.
|
// run, while the `y` fields stores the accumulated secondary extent.
|
||||||
let mut extent = Size2D::ZERO;
|
let mut extent = Size::ZERO;
|
||||||
let mut rotation = Vertical;
|
let mut rotation = Vertical;
|
||||||
|
|
||||||
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() {
|
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() {
|
||||||
@ -349,7 +348,7 @@ impl StackLayouter {
|
|||||||
// is reset for this new axis-aligned run.
|
// is reset for this new axis-aligned run.
|
||||||
if rotation != axes.secondary.axis() {
|
if rotation != axes.secondary.axis() {
|
||||||
extent.y = extent.x;
|
extent.y = extent.x;
|
||||||
extent.x = Size::ZERO;
|
extent.x = Length::ZERO;
|
||||||
rotation = axes.secondary.axis();
|
rotation = axes.secondary.axis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,11 +382,11 @@ impl StackLayouter {
|
|||||||
// The space in which this layout is aligned is given by the
|
// The space in which this layout is aligned is given by the
|
||||||
// distances between the borders of it's bounding box.
|
// distances between the borders of it's bounding box.
|
||||||
let usable =
|
let usable =
|
||||||
Size2D::new(bound.right - bound.left, bound.bottom - bound.top)
|
Size::new(bound.right - bound.left, bound.bottom - bound.top)
|
||||||
.generalized(axes);
|
.generalized(axes);
|
||||||
|
|
||||||
let local = usable.anchor(alignment, axes) - size.anchor(alignment, axes);
|
let local = usable.anchor(alignment, axes) - size.anchor(alignment, axes);
|
||||||
let pos = Size2D::new(bound.left, bound.top) + local.specialized(axes);
|
let pos = Size::new(bound.left, bound.top) + local.specialized(axes);
|
||||||
|
|
||||||
actions.add_layout(pos, layout);
|
actions.add_layout(pos, layout);
|
||||||
}
|
}
|
||||||
@ -417,15 +416,15 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Space {
|
impl Space {
|
||||||
fn new(index: usize, hard: bool, usable: Size2D) -> Space {
|
fn new(index: usize, hard: bool, usable: Size) -> Space {
|
||||||
Space {
|
Space {
|
||||||
index,
|
index,
|
||||||
hard,
|
hard,
|
||||||
layouts: vec![],
|
layouts: vec![],
|
||||||
size: Size2D::ZERO,
|
size: Size::ZERO,
|
||||||
usable,
|
usable,
|
||||||
extra: Size2D::ZERO,
|
extra: Size::ZERO,
|
||||||
rulers: ValueBox::with_all(Origin),
|
rulers: Value4::with_all(Origin),
|
||||||
last_spacing: LastSpacing::Hard,
|
last_spacing: LastSpacing::Hard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,10 @@ use toddle::query::{FontQuery, FontIndex};
|
|||||||
use toddle::tables::{CharMap, Header, HorizontalMetrics};
|
use toddle::tables::{CharMap, Header, HorizontalMetrics};
|
||||||
|
|
||||||
use crate::GlobalFontLoader;
|
use crate::GlobalFontLoader;
|
||||||
use crate::size::{Size, Size2D};
|
use crate::length::{Length, Size};
|
||||||
use crate::style::TextStyle;
|
use crate::style::TextStyle;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Performs the text layouting.
|
/// Performs the text layouting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TextLayouter<'a> {
|
struct TextLayouter<'a> {
|
||||||
@ -21,7 +20,7 @@ struct TextLayouter<'a> {
|
|||||||
actions: LayoutActions,
|
actions: LayoutActions,
|
||||||
buffer: String,
|
buffer: String,
|
||||||
active_font: FontIndex,
|
active_font: FontIndex,
|
||||||
width: Size,
|
width: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for text layouting.
|
/// The context for text layouting.
|
||||||
@ -54,7 +53,7 @@ impl<'a> TextLayouter<'a> {
|
|||||||
actions: LayoutActions::new(),
|
actions: LayoutActions::new(),
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
active_font: FontIndex::MAX,
|
active_font: FontIndex::MAX,
|
||||||
width: Size::ZERO,
|
width: Length::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ impl<'a> TextLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Layout {
|
Layout {
|
||||||
dimensions: Size2D::new(self.width, self.ctx.style.font_size()),
|
dimensions: Size::new(self.width, self.ctx.style.font_size()),
|
||||||
alignment: self.ctx.alignment,
|
alignment: self.ctx.alignment,
|
||||||
actions: self.actions.into_vec(),
|
actions: self.actions.into_vec(),
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ impl<'a> TextLayouter<'a> {
|
|||||||
|
|
||||||
/// Select the best font for a character and return its index along with
|
/// Select the best font for a character and return its index along with
|
||||||
/// the width of the char in the font.
|
/// the width of the char in the font.
|
||||||
async fn select_font(&mut self, c: char) -> Option<(FontIndex, Size)> {
|
async fn select_font(&mut self, c: char) -> Option<(FontIndex, Length)> {
|
||||||
let mut loader = self.ctx.loader.borrow_mut();
|
let mut loader = self.ctx.loader.borrow_mut();
|
||||||
|
|
||||||
let mut variant = self.ctx.style.variant;
|
let mut variant = self.ctx.style.variant;
|
||||||
@ -127,8 +126,8 @@ impl<'a> TextLayouter<'a> {
|
|||||||
if let Some((font, index)) = loader.get(query).await {
|
if let Some((font, index)) = loader.get(query).await {
|
||||||
// Determine the width of the char.
|
// Determine the width of the char.
|
||||||
let header = font.read_table::<Header>().ok()?;
|
let header = font.read_table::<Header>().ok()?;
|
||||||
let font_unit_ratio = 1.0 / (header.units_per_em as f32);
|
let font_unit_ratio = 1.0 / (header.units_per_em as f64);
|
||||||
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
|
let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
|
||||||
|
|
||||||
let glyph = font
|
let glyph = font
|
||||||
.read_table::<CharMap>()
|
.read_table::<CharMap>()
|
||||||
@ -139,7 +138,7 @@ impl<'a> TextLayouter<'a> {
|
|||||||
.read_table::<HorizontalMetrics>()
|
.read_table::<HorizontalMetrics>()
|
||||||
.ok()?
|
.ok()?
|
||||||
.get(glyph)?
|
.get(glyph)?
|
||||||
.advance_width as f32;
|
.advance_width as f64;
|
||||||
|
|
||||||
let char_width = font_unit_to_size(glyph_width)
|
let char_width = font_unit_to_size(glyph_width)
|
||||||
* self.ctx.style.font_size().to_pt();
|
* self.ctx.style.font_size().to_pt();
|
||||||
|
@ -8,173 +8,166 @@ use serde::Serialize;
|
|||||||
|
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
/// A general spacing type.
|
/// A general spacing type.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)]
|
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Size {
|
pub struct Length {
|
||||||
/// The size in typographic points (1/72 inches).
|
/// The length in typographic points (1/72 inches).
|
||||||
pub points: f32,
|
pub points: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size {
|
impl Length {
|
||||||
/// The zeroed size.
|
/// The zeroed length.
|
||||||
pub const ZERO: Size = Size { points: 0.0 };
|
pub const ZERO: Length = Length { points: 0.0 };
|
||||||
|
|
||||||
/// Create a size from an amount of points.
|
/// Create a length from an amount of points.
|
||||||
pub fn pt(points: f32) -> Size { Size { points } }
|
pub fn pt(points: f64) -> Length { Length { points } }
|
||||||
|
|
||||||
/// Create a size from an amount of millimeters.
|
/// Create a length from an amount of millimeters.
|
||||||
pub fn mm(mm: f32) -> Size { Size { points: 2.83465 * mm } }
|
pub fn mm(mm: f64) -> Length { Length { points: 2.83465 * mm } }
|
||||||
|
|
||||||
/// Create a size from an amount of centimeters.
|
/// Create a length from an amount of centimeters.
|
||||||
pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } }
|
pub fn cm(cm: f64) -> Length { Length { points: 28.3465 * cm } }
|
||||||
|
|
||||||
/// Create a size from an amount of inches.
|
/// Create a length from an amount of inches.
|
||||||
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } }
|
pub fn inches(inches: f64) -> Length { Length { points: 72.0 * inches } }
|
||||||
|
|
||||||
/// Convert this size into points.
|
/// Convert this length into points.
|
||||||
pub fn to_pt(self) -> f32 { self.points }
|
pub fn to_pt(self) -> f64 { self.points }
|
||||||
|
|
||||||
/// Convert this size into millimeters.
|
/// Convert this length into millimeters.
|
||||||
pub fn to_mm(self) -> f32 { self.points * 0.352778 }
|
pub fn to_mm(self) -> f64 { self.points * 0.352778 }
|
||||||
|
|
||||||
/// Convert this size into centimeters.
|
/// Convert this length into centimeters.
|
||||||
pub fn to_cm(self) -> f32 { self.points * 0.0352778 }
|
pub fn to_cm(self) -> f64 { self.points * 0.0352778 }
|
||||||
|
|
||||||
/// Convert this size into inches.
|
/// Convert this length into inches.
|
||||||
pub fn to_inches(self) -> f32 { self.points * 0.0138889 }
|
pub fn to_inches(self) -> f64 { self.points * 0.0138889 }
|
||||||
|
|
||||||
/// The maximum of this and the other size.
|
/// The maximum of this and the other length.
|
||||||
pub fn max(self, other: Size) -> Size {
|
pub fn max(self, other: Length) -> Length {
|
||||||
if self > other { self } else { other }
|
if self > other { self } else { other }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum of this and the other size.
|
/// The minimum of this and the other length.
|
||||||
pub fn min(self, other: Size) -> Size {
|
pub fn min(self, other: Length) -> Length {
|
||||||
if self <= other { self } else { other }
|
if self <= other { self } else { other }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set this size to the maximum of itself and the other size.
|
/// Set this length to the maximum of itself and the other length.
|
||||||
pub fn max_eq(&mut self, other: Size) { *self = self.max(other); }
|
pub fn max_eq(&mut self, other: Length) { *self = self.max(other); }
|
||||||
|
|
||||||
/// Set this size to the minimum of itself and the other size.
|
/// Set this length to the minimum of itself and the other length.
|
||||||
pub fn min_eq(&mut self, other: Size) { *self = self.min(other); }
|
pub fn min_eq(&mut self, other: Length) { *self = self.min(other); }
|
||||||
|
|
||||||
/// The anchor position along the given direction for an item with the given
|
/// The anchor position along the given direction for an item with the given
|
||||||
/// alignment in a container with this size.
|
/// alignment in a container with this length.
|
||||||
pub fn anchor(self, alignment: Alignment, direction: Direction) -> Size {
|
pub fn anchor(self, alignment: Alignment, direction: Direction) -> Length {
|
||||||
match (direction.is_positive(), alignment) {
|
match (direction.is_positive(), alignment) {
|
||||||
(true, Origin) | (false, End) => Size::ZERO,
|
(true, Origin) | (false, End) => Length::ZERO,
|
||||||
(_, Center) => self / 2,
|
(_, Center) => self / 2,
|
||||||
(true, End) | (false, Origin) => self,
|
(true, End) | (false, Origin) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Size {
|
impl Display for Length {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{}pt", self.points)
|
write!(f, "{}pt", self.points)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Size {
|
impl Debug for Length {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
Display::fmt(self, f)
|
Display::fmt(self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Neg for Size {
|
impl Neg for Length {
|
||||||
type Output = Size;
|
type Output = Length;
|
||||||
|
|
||||||
fn neg(self) -> Size {
|
fn neg(self) -> Length {
|
||||||
Size { points: -self.points }
|
Length { points: -self.points }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum for Size {
|
impl Sum for Length {
|
||||||
fn sum<I>(iter: I) -> Size
|
fn sum<I>(iter: I) -> Length
|
||||||
where I: Iterator<Item = Size> {
|
where I: Iterator<Item = Length> {
|
||||||
iter.fold(Size::ZERO, Add::add)
|
iter.fold(Length::ZERO, Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either an absolute size or a factor of some entity.
|
/// Either an absolute length or a factor of some entity.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum ScaleSize {
|
pub enum ScaleLength {
|
||||||
Absolute(Size),
|
Absolute(Length),
|
||||||
Scaled(f32),
|
Scaled(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScaleSize {
|
impl ScaleLength {
|
||||||
/// Use the absolute value or scale the entity.
|
/// Use the absolute value or scale the entity.
|
||||||
pub fn scaled(&self, entity: Size) -> Size {
|
pub fn scaled(&self, entity: Length) -> Length {
|
||||||
match self {
|
match self {
|
||||||
ScaleSize::Absolute(s) => *s,
|
ScaleLength::Absolute(s) => *s,
|
||||||
ScaleSize::Scaled(s) => *s * entity,
|
ScaleLength::Scaled(s) => *s * entity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ScaleSize {
|
impl Display for ScaleLength {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ScaleSize::Absolute(size) => write!(f, "{}", size),
|
ScaleLength::Absolute(length) => write!(f, "{}", length),
|
||||||
ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
ScaleLength::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for ScaleSize {
|
impl Debug for ScaleLength {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
Display::fmt(self, f)
|
Display::fmt(self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A scale size that is scaled by the font size.
|
|
||||||
pub type FSize = ScaleSize;
|
|
||||||
|
|
||||||
/// A scale size that is scaled by the size of the padded parent container.
|
|
||||||
pub type PSize = ScaleSize;
|
|
||||||
|
|
||||||
/// A value in two dimensions.
|
/// A value in two dimensions.
|
||||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||||
pub struct Value2D<T> {
|
pub struct Value2<T> {
|
||||||
/// The horizontal component.
|
/// The horizontal component.
|
||||||
pub x: T,
|
pub x: T,
|
||||||
/// The vertical component.
|
/// The vertical component.
|
||||||
pub y: T,
|
pub y: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Value2D<T> {
|
impl<T: Clone> Value2<T> {
|
||||||
/// Create a new 2D-value from two values.
|
/// Create a new 2D-value from two values.
|
||||||
pub fn new(x: T, y: T) -> Value2D<T> { Value2D { x, y } }
|
pub fn new(x: T, y: T) -> Value2<T> { Value2 { x, y } }
|
||||||
|
|
||||||
/// Create a new 2D-value with `x` set to a value and `y` to default.
|
/// Create a new 2D-value with `x` set to a value and `y` to default.
|
||||||
pub fn with_x(x: T) -> Value2D<T> where T: Default {
|
pub fn with_x(x: T) -> Value2<T> where T: Default {
|
||||||
Value2D { x, y: T::default() }
|
Value2 { x, y: T::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new 2D-value with `y` set to a value and `x` to default.
|
/// Create a new 2D-value with `y` set to a value and `x` to default.
|
||||||
pub fn with_y(y: T) -> Value2D<T> where T: Default {
|
pub fn with_y(y: T) -> Value2<T> where T: Default {
|
||||||
Value2D { x: T::default(), y }
|
Value2 { x: T::default(), y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new 2D-value with the primary axis set to a value and the other
|
/// Create a new 2D-value with the primary axis set to a value and the other
|
||||||
/// one to default.
|
/// one to default.
|
||||||
pub fn with_primary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default {
|
pub fn with_primary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
|
||||||
Value2D::with_x(v).generalized(axes)
|
Value2::with_x(v).generalized(axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new 2D-value with the secondary axis set to a value and the
|
/// Create a new 2D-value with the secondary axis set to a value and the
|
||||||
/// other one to default.
|
/// other one to default.
|
||||||
pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default {
|
pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
|
||||||
Value2D::with_y(v).generalized(axes)
|
Value2::with_y(v).generalized(axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a 2D-value with `x` and `y` set to the same value `s`.
|
/// Create a 2D-value with `x` and `y` set to the same value `s`.
|
||||||
pub fn with_all(s: T) -> Value2D<T> { Value2D { x: s.clone(), y: s } }
|
pub fn with_all(s: T) -> Value2<T> { Value2 { x: s.clone(), y: s } }
|
||||||
|
|
||||||
/// Get the specificed component.
|
/// Get the specificed component.
|
||||||
pub fn get(self, axis: SpecificAxis) -> T {
|
pub fn get(self, axis: SpecificAxis) -> T {
|
||||||
@ -216,16 +209,16 @@ impl<T: Clone> Value2D<T> {
|
|||||||
/// axes, that is:
|
/// axes, that is:
|
||||||
/// - `x` describes the primary axis instead of the horizontal one.
|
/// - `x` describes the primary axis instead of the horizontal one.
|
||||||
/// - `y` describes the secondary axis instead of the vertical one.
|
/// - `y` describes the secondary axis instead of the vertical one.
|
||||||
pub fn generalized(self, axes: LayoutAxes) -> Value2D<T> {
|
pub fn generalized(self, axes: LayoutAxes) -> Value2<T> {
|
||||||
match axes.primary.axis() {
|
match axes.primary.axis() {
|
||||||
Horizontal => self,
|
Horizontal => self,
|
||||||
Vertical => Value2D { x: self.y, y: self.x },
|
Vertical => Value2 { x: self.y, y: self.x },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the specialized version of this generalized Size2D (inverse to
|
/// Returns the specialized version of this generalized Size2D (inverse to
|
||||||
/// `generalized`).
|
/// `generalized`).
|
||||||
pub fn specialized(self, axes: LayoutAxes) -> Value2D<T> {
|
pub fn specialized(self, axes: LayoutAxes) -> Value2<T> {
|
||||||
// In fact, generalized is its own inverse. For reasons of clarity
|
// In fact, generalized is its own inverse. For reasons of clarity
|
||||||
// at the call site, we still have this second function.
|
// at the call site, we still have this second function.
|
||||||
self.generalized(axes)
|
self.generalized(axes)
|
||||||
@ -237,7 +230,7 @@ impl<T: Clone> Value2D<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Debug for Value2D<T> where T: Debug {
|
impl<T> Debug for Value2<T> where T: Debug {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.debug_list()
|
f.debug_list()
|
||||||
.entry(&self.x)
|
.entry(&self.x)
|
||||||
@ -247,83 +240,61 @@ impl<T> Debug for Value2D<T> where T: Debug {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A position or extent in 2-dimensional space.
|
/// A position or extent in 2-dimensional space.
|
||||||
pub type Size2D = Value2D<Size>;
|
pub type Size = Value2<Length>;
|
||||||
|
|
||||||
impl Size2D {
|
impl Size {
|
||||||
/// The zeroed 2D-size.
|
/// The zeroed 2D-length.
|
||||||
pub const ZERO: Size2D = Size2D { x: Size::ZERO, y: Size::ZERO };
|
pub const ZERO: Size = Size { x: Length::ZERO, y: Length::ZERO };
|
||||||
|
|
||||||
/// Whether the given 2D-size fits into this one, that is, both coordinate
|
/// Whether the given 2D-length fits into this one, that is, both coordinate
|
||||||
/// values are smaller or equal.
|
/// values are smaller or equal.
|
||||||
pub fn fits(self, other: Size2D) -> bool {
|
pub fn fits(self, other: Size) -> bool {
|
||||||
self.x >= other.x && self.y >= other.y
|
self.x >= other.x && self.y >= other.y
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a 2D-size padded by the paddings of the given box.
|
/// Return a 2D-length padded by the paddings of the given box.
|
||||||
pub fn padded(self, padding: SizeBox) -> Size2D {
|
pub fn padded(self, padding: Margins) -> Size {
|
||||||
Size2D {
|
Size {
|
||||||
x: self.x + padding.left + padding.right,
|
x: self.x + padding.left + padding.right,
|
||||||
y: self.y + padding.top + padding.bottom,
|
y: self.y + padding.top + padding.bottom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a 2D-size reduced by the paddings of the given box.
|
/// Return a 2D-length reduced by the paddings of the given box.
|
||||||
pub fn unpadded(self, padding: SizeBox) -> Size2D {
|
pub fn unpadded(self, padding: Margins) -> Size {
|
||||||
Size2D {
|
Size {
|
||||||
x: self.x - padding.left - padding.right,
|
x: self.x - padding.left - padding.right,
|
||||||
y: self.y - padding.top - padding.bottom,
|
y: self.y - padding.top - padding.bottom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The anchor position along the given axis for an item with the given
|
/// The anchor position along the given axis for an item with the given
|
||||||
/// alignment in a container with this size.
|
/// alignment in a container with this length.
|
||||||
///
|
///
|
||||||
/// This assumes the size to be generalized such that `x` corresponds to the
|
/// This assumes the length to be generalized such that `x` corresponds to the
|
||||||
/// primary axis.
|
/// primary axis.
|
||||||
pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size2D {
|
pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size {
|
||||||
Size2D {
|
Size {
|
||||||
x: self.x.anchor(alignment.primary, axes.primary),
|
x: self.x.anchor(alignment.primary, axes.primary),
|
||||||
y: self.y.anchor(alignment.secondary, axes.secondary),
|
y: self.y.anchor(alignment.secondary, axes.secondary),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Neg for Size2D {
|
impl Neg for Size {
|
||||||
type Output = Size2D;
|
type Output = Size;
|
||||||
|
|
||||||
fn neg(self) -> Size2D {
|
fn neg(self) -> Size {
|
||||||
Size2D {
|
Size {
|
||||||
x: -self.x,
|
x: -self.x,
|
||||||
y: -self.y,
|
y: -self.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A value that is stretchable in an interval from a minimal through an optimal
|
|
||||||
/// to a maximal value.
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
|
||||||
pub struct StretchValue<T> {
|
|
||||||
/// The minimum this value can be stretched to.
|
|
||||||
pub min: T,
|
|
||||||
/// The optimum for this value.
|
|
||||||
pub opt: T,
|
|
||||||
/// The maximum this value can be stretched to.
|
|
||||||
pub max: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StretchValue<T> {
|
|
||||||
/// Create a new stretch size from minimum, optimal and maximum values.
|
|
||||||
pub fn new(min: T, opt: T, max: T) -> StretchValue<T> {
|
|
||||||
StretchValue { min, opt, max }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A size that is stretchable.
|
|
||||||
pub type StretchSize = StretchValue<Size>;
|
|
||||||
|
|
||||||
/// A value in four dimensions.
|
/// A value in four dimensions.
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||||
pub struct ValueBox<T> {
|
pub struct Value4<T> {
|
||||||
/// The left extent.
|
/// The left extent.
|
||||||
pub left: T,
|
pub left: T,
|
||||||
/// The top extent.
|
/// The top extent.
|
||||||
@ -334,15 +305,15 @@ pub struct ValueBox<T> {
|
|||||||
pub bottom: T,
|
pub bottom: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> ValueBox<T> {
|
impl<T: Clone> Value4<T> {
|
||||||
/// Create a new box from four sizes.
|
/// Create a new box from four sizes.
|
||||||
pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> {
|
pub fn new(left: T, top: T, right: T, bottom: T) -> Value4<T> {
|
||||||
ValueBox { left, top, right, bottom }
|
Value4 { left, top, right, bottom }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a box with all four fields set to the same value `s`.
|
/// Create a box with all four fields set to the same value `s`.
|
||||||
pub fn with_all(value: T) -> ValueBox<T> {
|
pub fn with_all(value: T) -> Value4<T> {
|
||||||
ValueBox {
|
Value4 {
|
||||||
left: value.clone(),
|
left: value.clone(),
|
||||||
top: value.clone(),
|
top: value.clone(),
|
||||||
right: value.clone(),
|
right: value.clone(),
|
||||||
@ -369,7 +340,7 @@ impl<T: Clone> ValueBox<T> {
|
|||||||
|
|
||||||
/// Set all values to the given value.
|
/// Set all values to the given value.
|
||||||
pub fn set_all(&mut self, value: T) {
|
pub fn set_all(&mut self, value: T) {
|
||||||
*self = ValueBox::with_all(value);
|
*self = Value4::with_all(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the `left` and `right` values.
|
/// Set the `left` and `right` values.
|
||||||
@ -385,47 +356,47 @@ impl<T: Clone> ValueBox<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A size in four dimensions.
|
/// A length in four dimensions.
|
||||||
pub type SizeBox = ValueBox<Size>;
|
pub type Margins = Value4<Length>;
|
||||||
|
|
||||||
impl SizeBox {
|
impl Margins {
|
||||||
/// The zeroed size box.
|
/// The zeroed length box.
|
||||||
pub const ZERO: SizeBox = SizeBox {
|
pub const ZERO: Margins = Margins {
|
||||||
left: Size::ZERO,
|
left: Length::ZERO,
|
||||||
top: Size::ZERO,
|
top: Length::ZERO,
|
||||||
right: Size::ZERO,
|
right: Length::ZERO,
|
||||||
bottom: Size::ZERO,
|
bottom: Length::ZERO,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Size {
|
impl FromStr for Length {
|
||||||
type Err = ParseSizeError;
|
type Err = ParseLengthError;
|
||||||
|
|
||||||
fn from_str(src: &str) -> Result<Size, ParseSizeError> {
|
fn from_str(src: &str) -> Result<Length, ParseLengthError> {
|
||||||
let func = match () {
|
let func = match () {
|
||||||
_ if src.ends_with("pt") => Size::pt,
|
_ if src.ends_with("pt") => Length::pt,
|
||||||
_ if src.ends_with("mm") => Size::mm,
|
_ if src.ends_with("mm") => Length::mm,
|
||||||
_ if src.ends_with("cm") => Size::cm,
|
_ if src.ends_with("cm") => Length::cm,
|
||||||
_ if src.ends_with("in") => Size::inches,
|
_ if src.ends_with("in") => Length::inches,
|
||||||
_ => return Err(ParseSizeError(())),
|
_ => return Err(ParseLengthError),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(func(src[..src.len() - 2]
|
Ok(func(src[..src.len() - 2]
|
||||||
.parse::<f32>()
|
.parse::<f64>()
|
||||||
.map_err(|_| ParseSizeError(()))?))
|
.map_err(|_| ParseLengthError)?))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error which can be returned when parsing a size.
|
/// An error which can be returned when parsing a length.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct ParseSizeError(());
|
pub struct ParseLengthError;
|
||||||
|
|
||||||
impl std::error::Error for ParseSizeError {}
|
impl std::error::Error for ParseLengthError {}
|
||||||
|
|
||||||
impl Display for ParseSizeError {
|
impl Display for ParseLengthError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.write_str("invalid string for size")
|
f.write_str("invalid string for length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,7 +419,7 @@ macro_rules! implement_traits {
|
|||||||
})*
|
})*
|
||||||
|
|
||||||
$(implement_traits!(@$w i32, $ty $t $o $($rest)*);)*
|
$(implement_traits!(@$w i32, $ty $t $o $($rest)*);)*
|
||||||
$(implement_traits!(@$w f32, $ty $t $o $($rest)*);)*
|
$(implement_traits!(@$w f64, $ty $t $o $($rest)*);)*
|
||||||
};
|
};
|
||||||
|
|
||||||
(@front $num:ty, $ty:ident $t:ident $o:ident
|
(@front $num:ty, $ty:ident $t:ident $o:ident
|
||||||
@ -458,7 +429,7 @@ macro_rules! implement_traits {
|
|||||||
impl $tr<$ty> for $num {
|
impl $tr<$ty> for $num {
|
||||||
type Output = $ty;
|
type Output = $ty;
|
||||||
fn $tf($t, $o: $ty) -> $ty {
|
fn $tf($t, $o: $ty) -> $ty {
|
||||||
$ty { $($f: $tr::$tf($t as f32, $o.$f),)* }
|
$ty { $($f: $tr::$tf($t as f64, $o.$f),)* }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -470,12 +441,12 @@ macro_rules! implement_traits {
|
|||||||
impl $tr<$num> for $ty {
|
impl $tr<$num> for $ty {
|
||||||
type Output = $ty;
|
type Output = $ty;
|
||||||
fn $tf($t, $o: $num) -> $ty {
|
fn $tf($t, $o: $num) -> $ty {
|
||||||
$ty { $($f: $tr::$tf($t.$f, $o as f32),)* }
|
$ty { $($f: $tr::$tf($t.$f, $o as f64),)* }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $at<$num> for $ty {
|
impl $at<$num> for $ty {
|
||||||
fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f32);)* }
|
fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f64);)* }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -499,5 +470,5 @@ macro_rules! implement_size {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
implement_size! { Size(self, other) [points] }
|
implement_size! { Length(self, other) [points] }
|
||||||
implement_size! { Size2D(self, other) [x, y] }
|
implement_size! { Size(self, other) [x, y] }
|
35
src/lib.rs
35
src/lib.rs
@ -27,12 +27,11 @@ use toddle::{Font, OwnedData};
|
|||||||
use toddle::query::{FontLoader, SharedFontLoader};
|
use toddle::query::{FontLoader, SharedFontLoader};
|
||||||
use toddle::query::{FontProvider, FontIndex, FontDescriptor};
|
use toddle::query::{FontProvider, FontIndex, FontDescriptor};
|
||||||
|
|
||||||
use crate::problem::Problems;
|
use crate::diagnostic::Diagnostics;
|
||||||
use crate::layout::MultiLayout;
|
use crate::layout::MultiLayout;
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseState, parse};
|
use crate::syntax::{Decorations, SyntaxModel, Scope, ParseState, parse};
|
||||||
use crate::syntax::span::{Position, SpanVec, offset_spans};
|
use crate::syntax::span::{Offset, Pos};
|
||||||
|
|
||||||
|
|
||||||
/// Declare a module and reexport all its contents.
|
/// Declare a module and reexport all its contents.
|
||||||
macro_rules! pub_use_mod {
|
macro_rules! pub_use_mod {
|
||||||
@ -45,17 +44,17 @@ macro_rules! pub_use_mod {
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod problem;
|
pub mod diagnostic;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod func;
|
pub mod func;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod library;
|
pub mod library;
|
||||||
pub mod size;
|
pub mod length;
|
||||||
|
pub mod paper;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
|
|
||||||
/// Transforms source code into typesetted layouts.
|
/// Transforms source code into typesetted layouts.
|
||||||
///
|
///
|
||||||
/// A typesetter can be configured through various methods.
|
/// A typesetter can be configured through various methods.
|
||||||
@ -112,7 +111,7 @@ impl Typesetter {
|
|||||||
|
|
||||||
/// Parse source code into a syntax tree.
|
/// Parse source code into a syntax tree.
|
||||||
pub fn parse(&self, src: &str) -> Pass<SyntaxModel> {
|
pub fn parse(&self, src: &str) -> Pass<SyntaxModel> {
|
||||||
parse(src, Position::ZERO, &self.parse_state)
|
parse(src, Pos::ZERO, &self.parse_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a syntax tree and return the produced layout.
|
/// Layout a syntax tree and return the produced layout.
|
||||||
@ -176,18 +175,18 @@ impl<T> Pass<T> {
|
|||||||
/// User feedback data accumulated during a compilation pass.
|
/// User feedback data accumulated during a compilation pass.
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||||
pub struct Feedback {
|
pub struct Feedback {
|
||||||
/// Problems in the source code.
|
/// Diagnostics in the source code.
|
||||||
pub problems: Problems,
|
pub diagnostics: Diagnostics,
|
||||||
/// Decorations of the source code for semantic syntax highlighting.
|
/// Decorations of the source code for semantic syntax highlighting.
|
||||||
pub decos: SpanVec<Decoration>,
|
pub decorations: Decorations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Feedback {
|
impl Feedback {
|
||||||
/// Create a new feedback instance without errors and decos.
|
/// Create a new feedback instance without errors and decos.
|
||||||
pub fn new() -> Feedback {
|
pub fn new() -> Feedback {
|
||||||
Feedback {
|
Feedback {
|
||||||
problems: vec![],
|
diagnostics: vec![],
|
||||||
decos: vec![],
|
decorations: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,15 +198,15 @@ impl Feedback {
|
|||||||
|
|
||||||
/// Add other feedback data to this feedback.
|
/// Add other feedback data to this feedback.
|
||||||
pub fn extend(&mut self, other: Feedback) {
|
pub fn extend(&mut self, other: Feedback) {
|
||||||
self.problems.extend(other.problems);
|
self.diagnostics.extend(other.diagnostics);
|
||||||
self.decos.extend(other.decos);
|
self.decorations.extend(other.decorations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add more feedback whose spans are local and need to be offset by an
|
/// Add more feedback whose spans are local and need to be offset by an
|
||||||
/// `offset` to be correct in this feedback's context.
|
/// `offset` to be correct in this feedback's context.
|
||||||
pub fn extend_offset(&mut self, more: Feedback, offset: Position) {
|
pub fn extend_offset(&mut self, more: Feedback, offset: Pos) {
|
||||||
self.problems.extend(offset_spans(more.problems, offset));
|
self.diagnostics.extend(more.diagnostics.offset(offset));
|
||||||
self.decos.extend(offset_spans(more.decos, offset));
|
self.decorations.extend(more.decorations.offset(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use toddle::query::{FontWeight, FontStyle};
|
use toddle::query::{FontWeight, FontStyle};
|
||||||
use crate::size::FSize;
|
use crate::length::ScaleLength;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `font.family`: Set the font family.
|
/// `font.family`: Set the font family.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -13,17 +12,17 @@ function! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
let list = header.args.pos.get_all::<StringLike>(&mut f.problems)
|
let list = header.args.pos.get_all::<StringLike>(&mut f.diagnostics)
|
||||||
.map(|s| s.0.to_lowercase())
|
.map(|s| s.0.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tuples: Vec<_> = header.args.key
|
let tuples: Vec<_> = header.args.key
|
||||||
.get_all::<String, Tuple>(&mut f.problems)
|
.get_all::<String, Tuple>(&mut f.diagnostics)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let classes = tuples.into_iter()
|
let classes = tuples.into_iter()
|
||||||
.map(|(class, mut tuple)| {
|
.map(|(class, mut tuple)| {
|
||||||
let fallback = tuple.get_all::<StringLike>(&mut f.problems)
|
let fallback = tuple.get_all::<StringLike>(&mut f.diagnostics)
|
||||||
.map(|s| s.0.to_lowercase())
|
.map(|s| s.0.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
(class.to_lowercase(), fallback)
|
(class.to_lowercase(), fallback)
|
||||||
@ -64,8 +63,8 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
FontStyleFunc {
|
FontStyleFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
style: header.args.pos.get::<FontStyle>(&mut f.problems)
|
style: header.args.pos.get::<FontStyle>(&mut f.diagnostics)
|
||||||
.or_missing(&mut f.problems, header.name.span, "style"),
|
.or_missing(&mut f.diagnostics, header.name.span, "style"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ function! {
|
|||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
let body = body!(opt: body, ctx, f);
|
let body = body!(opt: body, ctx, f);
|
||||||
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.problems)
|
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.diagnostics)
|
||||||
.map(|Spanned { v: (weight, is_clamped), span }| {
|
.map(|Spanned { v: (weight, is_clamped), span }| {
|
||||||
if is_clamped {
|
if is_clamped {
|
||||||
warning!(
|
warning!(
|
||||||
@ -96,7 +95,7 @@ function! {
|
|||||||
|
|
||||||
weight
|
weight
|
||||||
})
|
})
|
||||||
.or_missing(&mut f.problems, header.name.span, "weight");
|
.or_missing(&mut f.diagnostics, header.name.span, "weight");
|
||||||
|
|
||||||
FontWeightFunc { body, weight }
|
FontWeightFunc { body, weight }
|
||||||
}
|
}
|
||||||
@ -111,25 +110,25 @@ function! {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct FontSizeFunc {
|
pub struct FontSizeFunc {
|
||||||
body: Option<SyntaxModel>,
|
body: Option<SyntaxModel>,
|
||||||
size: Option<FSize>,
|
size: Option<ScaleLength>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
FontSizeFunc {
|
FontSizeFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
size: header.args.pos.get::<FSize>(&mut f.problems)
|
size: header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
|
||||||
.or_missing(&mut f.problems, header.name.span, "size")
|
.or_missing(&mut f.diagnostics, header.name.span, "size")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, self.size, |t, s| {
|
styled(&self.body, ctx, self.size, |t, s| {
|
||||||
match s {
|
match s {
|
||||||
FSize::Absolute(size) => {
|
ScaleLength::Absolute(size) => {
|
||||||
t.base_font_size = size;
|
t.base_font_size = size;
|
||||||
t.font_scale = 1.0;
|
t.font_scale = 1.0;
|
||||||
}
|
}
|
||||||
FSize::Scaled(scale) => t.font_scale = scale,
|
ScaleLength::Scaled(scale) => t.font_scale = scale,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::size::PSize;
|
use crate::length::ScaleLength;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `align`: Aligns content along the layouting axes.
|
/// `align`: Aligns content along the layouting axes.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -13,14 +12,14 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
AlignFunc {
|
AlignFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
ctx.base = ctx.spaces[0].dimensions;
|
||||||
|
|
||||||
let map = self.map.dedup(&mut f.problems, ctx.axes, |alignment| {
|
let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |alignment| {
|
||||||
alignment.axis().map(|s| s.to_generic(ctx.axes))
|
alignment.axis().map(|s| s.to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -61,14 +60,14 @@ function! {
|
|||||||
DirectionFunc {
|
DirectionFunc {
|
||||||
name_span: header.name.span,
|
name_span: header.name.span,
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
ctx.base = ctx.spaces[0].dimensions;
|
||||||
|
|
||||||
let map = self.map.dedup(&mut f.problems, ctx.axes, |direction| {
|
let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |direction| {
|
||||||
Some(direction.axis().to_generic(ctx.axes))
|
Some(direction.axis().to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -103,15 +102,15 @@ function! {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct BoxFunc {
|
pub struct BoxFunc {
|
||||||
body: SyntaxModel,
|
body: SyntaxModel,
|
||||||
extents: AxisMap<PSize>,
|
extents: AxisMap<ScaleLength>,
|
||||||
debug: Option<bool>,
|
debug: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
BoxFunc {
|
BoxFunc {
|
||||||
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
||||||
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
|
||||||
debug: header.args.key.get::<bool>(&mut f.problems, "debug"),
|
debug: header.args.key.get::<bool>(&mut f.diagnostics, "debug"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,12 +122,12 @@ function! {
|
|||||||
ctx.debug = debug;
|
ctx.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = self.extents.dedup(&mut f.problems, ctx.axes);
|
let map = self.extents.dedup(&mut f.diagnostics, ctx.axes);
|
||||||
for &axis in &[Horizontal, Vertical] {
|
for &axis in &[Horizontal, Vertical] {
|
||||||
if let Some(psize) = map.get(axis) {
|
if let Some(scale) = map.get(axis) {
|
||||||
let size = psize.scaled(ctx.base.get(axis));
|
let length = scale.scaled(ctx.base.get(axis));
|
||||||
*ctx.base.get_mut(axis) = size;
|
*ctx.base.get_mut(axis) = length;
|
||||||
*ctx.spaces[0].dimensions.get_mut(axis) = size;
|
*ctx.spaces[0].dimensions.get_mut(axis) = length;
|
||||||
*ctx.spaces[0].expansion.get_mut(axis) = true;
|
*ctx.spaces[0].expansion.get_mut(axis) = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ pub_use_mod!(layout);
|
|||||||
pub_use_mod!(page);
|
pub_use_mod!(page);
|
||||||
pub_use_mod!(spacing);
|
pub_use_mod!(spacing);
|
||||||
|
|
||||||
|
|
||||||
/// Create a scope with all standard functions.
|
/// Create a scope with all standard functions.
|
||||||
pub fn std() -> Scope {
|
pub fn std() -> Scope {
|
||||||
let mut std = Scope::new::<ValFunc>();
|
let mut std = Scope::new::<ValFunc>();
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
use crate::size::Size;
|
use crate::length::Length;
|
||||||
use crate::style::{Paper, PaperClass};
|
use crate::paper::{Paper, PaperClass};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `page.size`: Set the size of pages.
|
/// `page.size`: Set the size of pages.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PageSizeFunc {
|
pub struct PageSizeFunc {
|
||||||
paper: Option<Paper>,
|
paper: Option<Paper>,
|
||||||
extents: AxisMap<Size>,
|
extents: AxisMap<Length>,
|
||||||
flip: bool,
|
flip: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, state, f) {
|
parse(header, body, state, f) {
|
||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
PageSizeFunc {
|
PageSizeFunc {
|
||||||
paper: header.args.pos.get::<Paper>(&mut f.problems),
|
paper: header.args.pos.get::<Paper>(&mut f.diagnostics),
|
||||||
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
|
||||||
flip: header.args.key.get::<bool>(&mut f.problems, "flip").unwrap_or(false),
|
flip: header.args.key.get::<bool>(&mut f.diagnostics, "flip").unwrap_or(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +25,12 @@ function! {
|
|||||||
|
|
||||||
if let Some(paper) = self.paper {
|
if let Some(paper) = self.paper {
|
||||||
style.class = paper.class;
|
style.class = paper.class;
|
||||||
style.dimensions = paper.dimensions;
|
style.dimensions = paper.size();
|
||||||
} else {
|
} else {
|
||||||
style.class = PaperClass::Custom;
|
style.class = PaperClass::Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = self.extents.dedup(&mut f.problems, ctx.axes);
|
let map = self.extents.dedup(&mut f.diagnostics, ctx.axes);
|
||||||
map.with(Horizontal, |&width| style.dimensions.x = width);
|
map.with(Horizontal, |&width| style.dimensions.x = width);
|
||||||
map.with(Vertical, |&height| style.dimensions.y = height);
|
map.with(Vertical, |&height| style.dimensions.y = height);
|
||||||
|
|
||||||
@ -53,13 +52,13 @@ function! {
|
|||||||
parse(header, body, state, f) {
|
parse(header, body, state, f) {
|
||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
PageMarginsFunc {
|
PageMarginsFunc {
|
||||||
padding: PaddingMap::parse(&mut f.problems, &mut header.args),
|
padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
let mut style = ctx.style.page;
|
let mut style = ctx.style.page;
|
||||||
self.padding.apply(&mut f.problems, ctx.axes, &mut style.margins);
|
self.padding.apply(&mut f.diagnostics, ctx.axes, &mut style.margins);
|
||||||
vec![SetPageStyle(style)]
|
vec![SetPageStyle(style)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use crate::size::FSize;
|
use crate::length::ScaleLength;
|
||||||
use crate::layout::SpacingKind;
|
use crate::layout::SpacingKind;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use self::ContentKind::*;
|
use self::ContentKind::*;
|
||||||
|
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `line.break`, `n`: Ends the current line.
|
/// `line.break`, `n`: Ends the current line.
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
@ -41,7 +40,7 @@ function! {
|
|||||||
pub struct ContentSpacingFunc {
|
pub struct ContentSpacingFunc {
|
||||||
body: Option<SyntaxModel>,
|
body: Option<SyntaxModel>,
|
||||||
content: ContentKind,
|
content: ContentKind,
|
||||||
spacing: Option<f32>,
|
spacing: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta = ContentKind;
|
type Meta = ContentKind;
|
||||||
@ -50,9 +49,9 @@ function! {
|
|||||||
ContentSpacingFunc {
|
ContentSpacingFunc {
|
||||||
body: body!(opt: body, state, f),
|
body: body!(opt: body, state, f),
|
||||||
content: meta,
|
content: meta,
|
||||||
spacing: header.args.pos.get::<f64>(&mut f.problems)
|
spacing: header.args.pos.get::<f64>(&mut f.diagnostics)
|
||||||
.map(|num| num as f32)
|
.map(|num| num as f64)
|
||||||
.or_missing(&mut f.problems, header.name.span, "spacing"),
|
.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +78,7 @@ function! {
|
|||||||
/// `spacing`, `h`, `v`: Adds spacing along an axis.
|
/// `spacing`, `h`, `v`: Adds spacing along an axis.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct SpacingFunc {
|
pub struct SpacingFunc {
|
||||||
spacing: Option<(AxisKey, FSize)>,
|
spacing: Option<(AxisKey, ScaleLength)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta = Option<SpecificAxis>;
|
type Meta = Option<SpecificAxis>;
|
||||||
@ -88,11 +87,11 @@ function! {
|
|||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
SpacingFunc {
|
SpacingFunc {
|
||||||
spacing: if let Some(axis) = meta {
|
spacing: if let Some(axis) = meta {
|
||||||
header.args.pos.get::<FSize>(&mut f.problems)
|
header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
|
||||||
.map(|s| (AxisKey::Specific(axis), s))
|
.map(|s| (AxisKey::Specific(axis), s))
|
||||||
} else {
|
} else {
|
||||||
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.problems)
|
header.args.key.get_with_key::<AxisKey, ScaleLength>(&mut f.diagnostics)
|
||||||
}.or_missing(&mut f.problems, header.name.span, "spacing"),
|
}.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
275
src/paper.rs
Normal file
275
src/paper.rs
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
//! Predefined papers.
|
||||||
|
|
||||||
|
use crate::length::{Length, Size, Value4, ScaleLength};
|
||||||
|
|
||||||
|
/// Specification of a paper.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct Paper {
|
||||||
|
/// The kind of paper, which defines the default margins.
|
||||||
|
pub class: PaperClass,
|
||||||
|
/// The width of the paper.
|
||||||
|
pub width: Length,
|
||||||
|
/// The height of the paper.
|
||||||
|
pub height: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Paper {
|
||||||
|
/// The paper with the given name.
|
||||||
|
pub fn from_name(name: &str) -> Option<Paper> {
|
||||||
|
parse_paper(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of the paper.
|
||||||
|
pub fn size(self) -> Size {
|
||||||
|
Size::new(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paper classes define default margins for a class of related papers.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum PaperClass {
|
||||||
|
Custom,
|
||||||
|
Base,
|
||||||
|
US,
|
||||||
|
Newspaper,
|
||||||
|
Book,
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
ScaleLength::Scaled(r),
|
||||||
|
ScaleLength::Scaled(b),
|
||||||
|
);
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! papers {
|
||||||
|
($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => {
|
||||||
|
$(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)*
|
||||||
|
|
||||||
|
fn parse_paper(paper: &str) -> Option<Paper> {
|
||||||
|
match paper.to_lowercase().as_str() {
|
||||||
|
$($($pats)* => Some($var),)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
|
||||||
|
#[doc = "Paper with name `"]
|
||||||
|
#[doc = $names]
|
||||||
|
#[doc = "`."]
|
||||||
|
pub const $var: Paper = Paper {
|
||||||
|
width: Length { points: 2.83465 * $width },
|
||||||
|
height: Length { points: 2.83465 * $height },
|
||||||
|
class: PaperClass::$class,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// All paper sizes in mm.
|
||||||
|
papers! {
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// ISO 216
|
||||||
|
|
||||||
|
// A Series
|
||||||
|
(PAPER_A0: Base, 841.0, 1189.0, "a0" | "poster")
|
||||||
|
(PAPER_A1: Base, 594.0, 841.0, "a1")
|
||||||
|
(PAPER_A2: Base, 420.0, 594.0, "a2")
|
||||||
|
(PAPER_A3: Base, 297.0, 420.0, "a3")
|
||||||
|
(PAPER_A4: Base, 210.0, 297.0, "a4")
|
||||||
|
(PAPER_A5: Base, 148.0, 210.0, "a5")
|
||||||
|
(PAPER_A6: Book, 105.0, 148.0, "a6")
|
||||||
|
(PAPER_A7: Base, 74.0, 105.0, "a7" | "iso-7810-id-2" | "id-2" | "visa" | "flyer")
|
||||||
|
(PAPER_A8: Base, 52.0, 74.0, "a8")
|
||||||
|
(PAPER_A9: Base, 37.0, 52.0, "a9")
|
||||||
|
(PAPER_A10: Base, 26.0, 37.0, "a10")
|
||||||
|
(PAPER_A11: Base, 18.0, 26.0, "a11")
|
||||||
|
|
||||||
|
// B Series
|
||||||
|
(PAPER_B1: Base, 707.0, 1000.0, "b1" | "flipchart")
|
||||||
|
(PAPER_B2: Base, 500.0, 707.0, "b2")
|
||||||
|
(PAPER_B3: Base, 353.0, 500.0, "b3")
|
||||||
|
(PAPER_B4: Base, 250.0, 353.0, "b4" | "sheet-music")
|
||||||
|
(PAPER_B5: Book, 176.0, 250.0, "b5")
|
||||||
|
(PAPER_B6: Book, 125.0, 176.0, "b6" | "book")
|
||||||
|
(PAPER_B7: Base, 88.0, 125.0, "b7" | "passport" | "iso-7810-id-3" | "id-3")
|
||||||
|
(PAPER_B8: Base, 62.0, 88.0, "b8")
|
||||||
|
|
||||||
|
// C Series
|
||||||
|
(PAPER_C3: Base, 324.0, 458.0, "c3")
|
||||||
|
(PAPER_C4: Base, 229.0, 324.0, "c4")
|
||||||
|
(PAPER_C5: Base, 162.0, 229.0, "c5")
|
||||||
|
(PAPER_C6: Base, 114.0, 162.0, "c6")
|
||||||
|
(PAPER_C7: Base, 81.0, 114.0, "c7")
|
||||||
|
(PAPER_C8: Base, 57.0, 81.0, "c8")
|
||||||
|
|
||||||
|
// D Series (DIN extension to ISO)
|
||||||
|
(PAPER_D3: Base, 272.0, 385.0, "din-d3")
|
||||||
|
(PAPER_D4: Base, 192.0, 272.0, "din-d4")
|
||||||
|
(PAPER_D5: Base, 136.0, 192.0, "din-d5" | "dvd")
|
||||||
|
(PAPER_D6: Base, 96.0, 136.0, "din-d6")
|
||||||
|
(PAPER_D7: Base, 68.0, 96.0, "din-d7")
|
||||||
|
(PAPER_D8: Base, 48.0, 68.0, "din-d8")
|
||||||
|
|
||||||
|
// Academically relevant SIS extensions
|
||||||
|
(PAPER_G5: Base, 169.0, 239.0, "sis-g5")
|
||||||
|
(PAPER_E5: Base, 115.0, 220.0, "sis-e5")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// Unites States
|
||||||
|
|
||||||
|
// Customary
|
||||||
|
(PAPER_FOLIO: US, 210.0, 330.0, "folio" | "us-folio" | "us-f4")
|
||||||
|
(PAPER_LETTER: US, 216.0, 279.0, "letter" | "ansi-a" |
|
||||||
|
"american-quarto" | "carta")
|
||||||
|
(PAPER_LEGAL: US, 216.0, 356.0, "legal")
|
||||||
|
(PAPER_TABLOID: Newspaper, 279.0, 432.0, "tabloid" | "ansi-b")
|
||||||
|
(PAPER_LEDGER: Base, 432.0, 279.0, "ledger")
|
||||||
|
(PAPER_JUNIOR_LEGAL: US, 127.0, 203.0, "junior-legal" | "index-card")
|
||||||
|
(PAPER_HALF_LETTER: Base, 140.0, 216.0, "half-letter")
|
||||||
|
(PAPER_GOVERNMENT_LETTER: US, 203.0, 267.0, "government-letter")
|
||||||
|
(PAPER_GOVERNMENT_LEGAL: US, 216.0, 330.0, "government-legal" | "officio")
|
||||||
|
|
||||||
|
// ANSI Extensions
|
||||||
|
(PAPER_ANSI_C: Base, 432.0, 559.0, "ansi-c")
|
||||||
|
(PAPER_ANSI_D: Base, 559.0, 864.0, "ansi-d")
|
||||||
|
(PAPER_ANSI_E: Base, 864.0, 1118.0, "ansi-e")
|
||||||
|
(PAPER_ENGINEERING_F: Base, 711.0, 1016.0, "engineering-f" | "engineering" |
|
||||||
|
"navfac" | "aerospace")
|
||||||
|
|
||||||
|
// Architectural Paper
|
||||||
|
(PAPER_ARCH_A: Base, 229.0, 305.0, "arch-a" | "arch-1")
|
||||||
|
(PAPER_ARCH_B: Base, 305.0, 457.0, "arch-b" | "arch-2" | "extra-tabloide")
|
||||||
|
(PAPER_ARCH_C: Base, 457.0, 610.0, "arch-c" | "arch-3")
|
||||||
|
(PAPER_ARCH_D: Base, 610.0, 914.0, "arch-d" | "arch-4")
|
||||||
|
(PAPER_ARCH_E1: Base, 762.0, 1067.0, "arch-e1" | "arch-5")
|
||||||
|
(PAPER_ARCH_E: Base, 914.0, 1219.0, "arch-e" | "arch-6")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// Japan
|
||||||
|
|
||||||
|
// JIS B Series
|
||||||
|
(PAPER_JIS_B0: Base, 1030.0, 1456.0, "jis-b0" | "jb0")
|
||||||
|
(PAPER_JIS_B1: Base, 728.0, 1030.0, "jis-b1" | "jb1")
|
||||||
|
(PAPER_JIS_B2: Base, 515.0, 728.0, "jis-b2" | "jb2")
|
||||||
|
(PAPER_JIS_B3: Base, 364.0, 515.0, "jis-b3" | "jb3")
|
||||||
|
(PAPER_JIS_B4: Base, 257.0, 364.0, "jis-b4" | "jb4")
|
||||||
|
(PAPER_JIS_B5: Base, 182.0, 257.0, "jis-b5" | "jb5")
|
||||||
|
(PAPER_JIS_B6: Base, 128.0, 182.0, "jis-b6" | "jb6")
|
||||||
|
(PAPER_JIS_B7: Base, 91.0, 128.0, "jis-b7" | "jb7")
|
||||||
|
(PAPER_JIS_B8: Base, 64.0, 91.0, "jis-b8" | "jb8")
|
||||||
|
(PAPER_JIS_B9: Base, 45.0, 64.0, "jis-b9" | "jb9")
|
||||||
|
(PAPER_JIS_B10: Base, 32.0, 45.0, "jis-b10" | "jb10")
|
||||||
|
(PAPER_JIS_B11: Base, 22.0, 32.0, "jis-b11" | "jb11")
|
||||||
|
|
||||||
|
// Traditional
|
||||||
|
(PAPER_SHIROKU_BAN_4: Base, 264.0, 379.0, "shiroku-ban-4")
|
||||||
|
(PAPER_SHIROKU_BAN_5: Base, 189.0, 262.0, "shiroku-ban-5")
|
||||||
|
(PAPER_SHIROKU_BAN_6: Base, 127.0, 188.0, "shiroku-ban-6")
|
||||||
|
(PAPER_KIKU_4: Base, 227.0, 306.0, "kiku-4")
|
||||||
|
(PAPER_KIKU_5: Base, 151.0, 227.0, "kiku-5")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// China
|
||||||
|
|
||||||
|
// Chinese D Series
|
||||||
|
(PAPER_SAC_D0: Base, 764.0, 1064.0, "sac-d0" | "cn-d0")
|
||||||
|
(PAPER_SAC_D1: Base, 532.0, 760.0, "sac-d1" | "cn-d1")
|
||||||
|
(PAPER_SAC_D2: Base, 380.0, 528.0, "sac-d2" | "cn-d2")
|
||||||
|
(PAPER_SAC_D3: Base, 264.0, 376.0, "sac-d3" | "cn-d3")
|
||||||
|
(PAPER_SAC_D4: Base, 188.0, 260.0, "sac-d4" | "cn-d4")
|
||||||
|
(PAPER_SAC_D5: Base, 130.0, 184.0, "sac-d5" | "cn-d5")
|
||||||
|
(PAPER_SAC_D6: Base, 92.0, 126.0, "sac-d6" | "cn-d6")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// United Kingdom Imperial (Assortment)
|
||||||
|
|
||||||
|
(PAPER_MONARCH: Base, 184.0, 267.0, "monarch")
|
||||||
|
(PAPER_QUARTO: Base, 229.0, 279.0, "quarto" | "us-quarto")
|
||||||
|
(PAPER_UK_QUARTO: Base, 203.0, 254.0, "uk-quarto" | "imperial-quarto")
|
||||||
|
(PAPER_UK_FOOLSCAP: Base, 343.0, 432.0, "foolscap" | "us-foolscap")
|
||||||
|
(PAPER_FOOLSCAP: Base, 203.0, 330.0, "imperial-foolscap" | "uk-foolscap")
|
||||||
|
(PAPER_POTT: Base, 318.0, 381.0, "pott")
|
||||||
|
(PAPER_CROWN: Base, 318.0, 508.0, "crown")
|
||||||
|
(PAPER_PINCHED_POST: Base, 375.0, 470.0, "pinched-post")
|
||||||
|
(PAPER_POST: Base, 394.0, 489.0, "post")
|
||||||
|
(PAPER_LARGE_POST: Base, 419.0, 533.0, "large-post")
|
||||||
|
(PAPER_DEMY: Base, 445.0, 572.0, "demy")
|
||||||
|
(PAPER_ROYAL: Base, 508.0, 635.0, "royal")
|
||||||
|
(PAPER_DOUBLE_CROWN: Base, 508.0, 762.0, "double-crown" | "theatre")
|
||||||
|
(PAPER_ELEPHANT: Base, 584.0, 711.0, "elephant")
|
||||||
|
(PAPER_DOUBLE_ROYAL: Base, 635.0, 1016.0, "double-royal" | "rail")
|
||||||
|
(PAPER_QUAD_CROWN: Base, 762.0, 1016.0, "quad-crown" | "cinema")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// French Traditional (AFNOR)
|
||||||
|
|
||||||
|
(PAPER_CLOCHE: Base, 300.0, 400.0, "cloche")
|
||||||
|
(PAPER_POT: Base, 310.0, 400.0, "pot" | "ecolier" | "écolier")
|
||||||
|
(PAPER_TELLIERE: Base, 340.0, 440.0, "telliere" | "tellière")
|
||||||
|
(PAPER_COURONNE_ECRITURE: Base, 360.0, 460.0, "couronne-ecriture" |
|
||||||
|
"couronne" | "couronne-écriture")
|
||||||
|
(PAPER_COURONNE_EDITION: Base, 370.0, 470.0, "couronne-edition" |
|
||||||
|
"couronne-édition")
|
||||||
|
(PAPER_ROBERTO: Base, 390.0, 500.0, "roberto")
|
||||||
|
(PAPER_ECU: Base, 400.0, 520.0, "ecu" | "écu")
|
||||||
|
(PAPER_COQUILLE: Base, 440.0, 560.0, "coquille")
|
||||||
|
(PAPER_CARRE: Base, 450.0, 560.0, "carre" | "carré")
|
||||||
|
(PAPER_CAVALIER: Base, 460.0, 620.0, "cavalier")
|
||||||
|
(PAPER_DEMI_RAISIN: Base, 325.0, 500.0, "demi-raisin")
|
||||||
|
(PAPER_RAISIN: Base, 500.0, 650.0, "raisin" | "dessin")
|
||||||
|
(PAPER_DOUBLE_RAISIN: Base, 650.0, 1000.0, "double-raisin")
|
||||||
|
(PAPER_JESUS: Base, 560.0, 760.0, "jesus" | "jésus")
|
||||||
|
(PAPER_SOLEIL: Base, 600.0, 800.0, "soleil")
|
||||||
|
(PAPER_COLOMBIER_AFFICHE: Base, 600.0, 800.0, "colombier-affiche" | "affiche")
|
||||||
|
(PAPER_COLOMBIER_COMMERCIAL: Base, 630.0, 900.0, "colombier-commercial")
|
||||||
|
(PAPER_PETIT_AIGLE: Base, 700.0, 940.0, "petit-aigle")
|
||||||
|
(PAPER_GRAND_AIGLE: Base, 750.0, 1060.0, "grand-aigle" | "napoleon")
|
||||||
|
(PAPER_GRAND_MONDE: Base, 900.0, 1260.0, "grand-monde")
|
||||||
|
(PAPER_UNIVERS: Base, 1000.0, 1300.0, "univers" | "universe")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// Newspaper
|
||||||
|
|
||||||
|
(PAPER_COMPACT: Newspaper, 280.0, 430.0, "compact")
|
||||||
|
(PAPER_BERLINER: Newspaper, 315.0, 470.0, "berliner" | "midi")
|
||||||
|
(PAPER_RHENISH: Newspaper, 350.0, 520.0, "rhenish")
|
||||||
|
(PAPER_BROADSHEET: Newspaper, 381.0, 578.0, "broadsheet" | "newspaper")
|
||||||
|
(PAPER_NEW_YORK_TIMES: Newspaper, 305.0, 559.0, "new-york-times" | "times")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// Books
|
||||||
|
|
||||||
|
(PAPER_FOLIO_BOOK: Book, 304.8, 482.6, "book-folio")
|
||||||
|
(PAPER_QUARTO_BOOK: Book, 241.3, 304.8, "book-quarto")
|
||||||
|
(PAPER_OCTAVO_BOOK: Book, 152.4, 228.6, "book-octavo")
|
||||||
|
(PAPER_16_MO_BOOK: Book, 101.6, 171.45, "book-16mo")
|
||||||
|
(PAPER_32_MO_BOOK: Book, 88.9, 139.7, "book-32mo")
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
// Various
|
||||||
|
|
||||||
|
(PAPER_ID_1: Base, 85.6, 53.98, "id-card" | "id-1" | "iso-7810-id-1" |
|
||||||
|
"eu-business-card" | "business-card")
|
||||||
|
(PAPER_US_BUSINESS_CARD: Base, 88.9, 50.8, "us-business-card")
|
||||||
|
(PAPER_JP_BUSINESS_CARD: Base, 91.0, 55.0, "jp-business-card")
|
||||||
|
(PAPER_CN_BUSINESS_CARD: Base, 90.0, 54.0, "cn-business-card")
|
||||||
|
(PAPER_A4_16_9: Base, 297.0, 167.0625, "presentation-16-9" | "presentation")
|
||||||
|
(PAPER_A4_4_3: Base, 280.0, 210.0, "presentation-4-3")
|
||||||
|
(PAPER_POSTCARD: Base, 152.4, 101.6, "postcard")
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
//! Problems (errors / warnings) in _Typst_ documents.
|
|
||||||
//!
|
|
||||||
//! There are no fatal errors in _Typst_. 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.
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use crate::syntax::span::SpanVec;
|
|
||||||
|
|
||||||
|
|
||||||
/// A list of spanned problems.
|
|
||||||
pub type Problems = SpanVec<Problem>;
|
|
||||||
|
|
||||||
/// A problem that arose in parsing or layouting.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
|
|
||||||
pub struct Problem {
|
|
||||||
/// How severe / important the problem is.
|
|
||||||
pub severity: Severity,
|
|
||||||
/// A message describing the problem.
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How severe / important a problem is.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum Severity {
|
|
||||||
/// Something in the code is not good.
|
|
||||||
Warning,
|
|
||||||
/// Something in the code is wrong!
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Problem {
|
|
||||||
/// Create a new problem from message and severity.
|
|
||||||
pub fn new(message: impl Into<String>, severity: Severity) -> Self {
|
|
||||||
Self { message: message.into(), severity }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a problem with `Error` severity.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use typstc::error;
|
|
||||||
/// # use typstc::syntax::span::Span;
|
|
||||||
/// # use typstc::Feedback;
|
|
||||||
/// # let span = Span::ZERO;
|
|
||||||
/// # let mut feedback = Feedback::new();
|
|
||||||
/// # let name = "";
|
|
||||||
/// // Create formatted error values.
|
|
||||||
/// let error = error!("expected {}", name);
|
|
||||||
///
|
|
||||||
/// // Create spanned errors.
|
|
||||||
/// let spanned = error!(span, "there is an error here");
|
|
||||||
///
|
|
||||||
/// // Create an error and directly add it to existing feedback.
|
|
||||||
/// error!(@feedback, span, "oh no!");
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
($($tts:tt)*) => {
|
|
||||||
$crate::__impl_problem!($crate::problem::Severity::Error; $($tts)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a problem with `Warning` severity.
|
|
||||||
///
|
|
||||||
/// This works exactly like `error!`. See its documentation for more
|
|
||||||
/// information.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warning {
|
|
||||||
($($tts:tt)*) => {
|
|
||||||
$crate::__impl_problem!($crate::problem::Severity::Warning; $($tts)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Backs the `error!` and `warning!` macros.
|
|
||||||
#[macro_export]
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! __impl_problem {
|
|
||||||
($severity:expr; @$feedback:expr, $($tts:tt)*) => {
|
|
||||||
$feedback.problems.push($crate::__impl_problem!($severity; $($tts)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
($severity:expr; $fmt:literal $($tts:tt)*) => {
|
|
||||||
$crate::problem::Problem::new(format!($fmt $($tts)*), $severity)
|
|
||||||
};
|
|
||||||
|
|
||||||
($severity:expr; $span:expr, $fmt:literal $($tts:tt)*) => {
|
|
||||||
$crate::syntax::span::Spanned::new(
|
|
||||||
$crate::__impl_problem!($severity; $fmt $($tts)*),
|
|
||||||
$span,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
303
src/style.rs
303
src/style.rs
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use toddle::fallback;
|
use toddle::fallback;
|
||||||
use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight};
|
use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight};
|
||||||
use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
|
use crate::length::{Length, Size, Margins, Value4, ScaleLength};
|
||||||
|
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
||||||
|
|
||||||
/// Defines properties of pages and text.
|
/// Defines properties of pages and text.
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
@ -25,35 +25,35 @@ pub struct TextStyle {
|
|||||||
/// whether the next `*` adds or removes font weight.
|
/// whether the next `*` adds or removes font weight.
|
||||||
pub bolder: bool,
|
pub bolder: bool,
|
||||||
/// The base font size.
|
/// The base font size.
|
||||||
pub base_font_size: Size,
|
pub base_font_size: Length,
|
||||||
/// The font scale to apply on the base font size.
|
/// The font scale to apply on the base font size.
|
||||||
pub font_scale: f32,
|
pub font_scale: f64,
|
||||||
/// The word spacing (as a multiple of the font size).
|
/// The word spacing (as a multiple of the font size).
|
||||||
pub word_spacing_scale: f32,
|
pub word_spacing_scale: f64,
|
||||||
/// The line spacing (as a multiple of the font size).
|
/// The line spacing (as a multiple of the font size).
|
||||||
pub line_spacing_scale: f32,
|
pub line_spacing_scale: f64,
|
||||||
/// The paragraphs spacing (as a multiple of the font size).
|
/// The paragraphs spacing (as a multiple of the font size).
|
||||||
pub paragraph_spacing_scale: f32,
|
pub paragraph_spacing_scale: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextStyle {
|
impl TextStyle {
|
||||||
/// The scaled font size.
|
/// The scaled font size.
|
||||||
pub fn font_size(&self) -> Size {
|
pub fn font_size(&self) -> Length {
|
||||||
self.base_font_size * self.font_scale
|
self.base_font_size * self.font_scale
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute word spacing.
|
/// The absolute word spacing.
|
||||||
pub fn word_spacing(&self) -> Size {
|
pub fn word_spacing(&self) -> Length {
|
||||||
self.word_spacing_scale * self.font_size()
|
self.word_spacing_scale * self.font_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute line spacing.
|
/// The absolute line spacing.
|
||||||
pub fn line_spacing(&self) -> Size {
|
pub fn line_spacing(&self) -> Length {
|
||||||
(self.line_spacing_scale - 1.0) * self.font_size()
|
(self.line_spacing_scale - 1.0) * self.font_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute paragraph spacing.
|
/// The absolute paragraph spacing.
|
||||||
pub fn paragraph_spacing(&self) -> Size {
|
pub fn paragraph_spacing(&self) -> Length {
|
||||||
(self.paragraph_spacing_scale - 1.0) * self.font_size()
|
(self.paragraph_spacing_scale - 1.0) * self.font_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ impl Default for TextStyle {
|
|||||||
weight: FontWeight(400),
|
weight: FontWeight(400),
|
||||||
},
|
},
|
||||||
bolder: false,
|
bolder: false,
|
||||||
base_font_size: Size::pt(11.0),
|
base_font_size: Length::pt(11.0),
|
||||||
font_scale: 1.0,
|
font_scale: 1.0,
|
||||||
word_spacing_scale: 0.25,
|
word_spacing_scale: 0.25,
|
||||||
line_spacing_scale: 1.2,
|
line_spacing_scale: 1.2,
|
||||||
@ -92,10 +92,10 @@ pub struct PageStyle {
|
|||||||
/// The class of this page.
|
/// The class of this page.
|
||||||
pub class: PaperClass,
|
pub class: PaperClass,
|
||||||
/// The width and height of the page.
|
/// The width and height of the page.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size,
|
||||||
/// The amount of white space on each side. If a side is set to `None`, the
|
/// The amount of white space on each side. If a side is set to `None`, the
|
||||||
/// default for the paper class is used.
|
/// default for the paper class is used.
|
||||||
pub margins: ValueBox<Option<PSize>>,
|
pub margins: Value4<Option<ScaleLength>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PageStyle {
|
impl PageStyle {
|
||||||
@ -103,17 +103,17 @@ impl PageStyle {
|
|||||||
pub fn new(paper: Paper) -> PageStyle {
|
pub fn new(paper: Paper) -> PageStyle {
|
||||||
PageStyle {
|
PageStyle {
|
||||||
class: paper.class,
|
class: paper.class,
|
||||||
dimensions: paper.dimensions,
|
dimensions: paper.size(),
|
||||||
margins: ValueBox::with_all(None),
|
margins: Value4::with_all(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute margins.
|
/// The absolute margins.
|
||||||
pub fn margins(&self) -> SizeBox {
|
pub fn margins(&self) -> Margins {
|
||||||
let dims = self.dimensions;
|
let dims = self.dimensions;
|
||||||
let default = self.class.default_margins();
|
let default = self.class.default_margins();
|
||||||
|
|
||||||
SizeBox {
|
Margins {
|
||||||
left: self.margins.left.unwrap_or(default.left).scaled(dims.x),
|
left: self.margins.left.unwrap_or(default.left).scaled(dims.x),
|
||||||
top: self.margins.top.unwrap_or(default.top).scaled(dims.y),
|
top: self.margins.top.unwrap_or(default.top).scaled(dims.y),
|
||||||
right: self.margins.right.unwrap_or(default.right).scaled(dims.x),
|
right: self.margins.right.unwrap_or(default.right).scaled(dims.x),
|
||||||
@ -127,270 +127,3 @@ impl Default for PageStyle {
|
|||||||
PageStyle::new(PAPER_A4)
|
PageStyle::new(PAPER_A4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Details about a type of paper.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
pub struct Paper {
|
|
||||||
/// The kind of paper, which defines the default margins.
|
|
||||||
pub class: PaperClass,
|
|
||||||
/// The size of the paper.
|
|
||||||
pub dimensions: Size2D,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Paper {
|
|
||||||
/// The paper with the given name.
|
|
||||||
pub fn from_name(name: &str) -> Option<Paper> {
|
|
||||||
parse_paper(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Paper classes define default margins for a class of related papers.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum PaperClass {
|
|
||||||
Custom,
|
|
||||||
Base,
|
|
||||||
US,
|
|
||||||
Newspaper,
|
|
||||||
Book,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaperClass {
|
|
||||||
/// The default margins for this page class.
|
|
||||||
pub fn default_margins(self) -> ValueBox<PSize> {
|
|
||||||
use PaperClass::*;
|
|
||||||
let values = |l, t, r, b| ValueBox::new(
|
|
||||||
PSize::Scaled(l),
|
|
||||||
PSize::Scaled(t),
|
|
||||||
PSize::Scaled(r),
|
|
||||||
PSize::Scaled(b),
|
|
||||||
);
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! papers {
|
|
||||||
($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => {
|
|
||||||
$(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)*
|
|
||||||
|
|
||||||
fn parse_paper(paper: &str) -> Option<Paper> {
|
|
||||||
match paper.to_lowercase().as_str() {
|
|
||||||
$($($pats)* => Some($var),)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
|
|
||||||
#[doc = "Paper with name `"]
|
|
||||||
#[doc = $names]
|
|
||||||
#[doc = "`."]
|
|
||||||
pub const $var: Paper = Paper {
|
|
||||||
dimensions: Size2D {
|
|
||||||
x: Size { points: 2.83465 * $width },
|
|
||||||
y: Size { points: 2.83465 * $height },
|
|
||||||
},
|
|
||||||
class: PaperClass::$class,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// All paper sizes in mm.
|
|
||||||
papers! {
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// ISO 216
|
|
||||||
|
|
||||||
// A Series
|
|
||||||
(PAPER_A0: Base, 841.0, 1189.0, "a0" | "poster")
|
|
||||||
(PAPER_A1: Base, 594.0, 841.0, "a1")
|
|
||||||
(PAPER_A2: Base, 420.0, 594.0, "a2")
|
|
||||||
(PAPER_A3: Base, 297.0, 420.0, "a3")
|
|
||||||
(PAPER_A4: Base, 210.0, 297.0, "a4")
|
|
||||||
(PAPER_A5: Base, 148.0, 210.0, "a5")
|
|
||||||
(PAPER_A6: Book, 105.0, 148.0, "a6")
|
|
||||||
(PAPER_A7: Base, 74.0, 105.0, "a7" | "iso-7810-id-2" | "id-2" | "visa" | "flyer")
|
|
||||||
(PAPER_A8: Base, 52.0, 74.0, "a8")
|
|
||||||
(PAPER_A9: Base, 37.0, 52.0, "a9")
|
|
||||||
(PAPER_A10: Base, 26.0, 37.0, "a10")
|
|
||||||
(PAPER_A11: Base, 18.0, 26.0, "a11")
|
|
||||||
|
|
||||||
// B Series
|
|
||||||
(PAPER_B1: Base, 707.0, 1000.0, "b1" | "flipchart")
|
|
||||||
(PAPER_B2: Base, 500.0, 707.0, "b2")
|
|
||||||
(PAPER_B3: Base, 353.0, 500.0, "b3")
|
|
||||||
(PAPER_B4: Base, 250.0, 353.0, "b4" | "sheet-music")
|
|
||||||
(PAPER_B5: Book, 176.0, 250.0, "b5")
|
|
||||||
(PAPER_B6: Book, 125.0, 176.0, "b6" | "book")
|
|
||||||
(PAPER_B7: Base, 88.0, 125.0, "b7" | "passport" | "iso-7810-id-3" | "id-3")
|
|
||||||
(PAPER_B8: Base, 62.0, 88.0, "b8")
|
|
||||||
|
|
||||||
// C Series
|
|
||||||
(PAPER_C3: Base, 324.0, 458.0, "c3")
|
|
||||||
(PAPER_C4: Base, 229.0, 324.0, "c4")
|
|
||||||
(PAPER_C5: Base, 162.0, 229.0, "c5")
|
|
||||||
(PAPER_C6: Base, 114.0, 162.0, "c6")
|
|
||||||
(PAPER_C7: Base, 81.0, 114.0, "c7")
|
|
||||||
(PAPER_C8: Base, 57.0, 81.0, "c8")
|
|
||||||
|
|
||||||
// D Series (DIN extension to ISO)
|
|
||||||
(PAPER_D3: Base, 272.0, 385.0, "din-d3")
|
|
||||||
(PAPER_D4: Base, 192.0, 272.0, "din-d4")
|
|
||||||
(PAPER_D5: Base, 136.0, 192.0, "din-d5" | "dvd")
|
|
||||||
(PAPER_D6: Base, 96.0, 136.0, "din-d6")
|
|
||||||
(PAPER_D7: Base, 68.0, 96.0, "din-d7")
|
|
||||||
(PAPER_D8: Base, 48.0, 68.0, "din-d8")
|
|
||||||
|
|
||||||
// Academically relevant SIS extensions
|
|
||||||
(PAPER_G5: Base, 169.0, 239.0, "sis-g5")
|
|
||||||
(PAPER_E5: Base, 115.0, 220.0, "sis-e5")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// Unites States
|
|
||||||
|
|
||||||
// Customary
|
|
||||||
(PAPER_FOLIO: US, 210.0, 330.0, "folio" | "us-folio" | "us-f4")
|
|
||||||
(PAPER_LETTER: US, 216.0, 279.0, "letter" | "ansi-a" |
|
|
||||||
"american-quarto" | "carta")
|
|
||||||
(PAPER_LEGAL: US, 216.0, 356.0, "legal")
|
|
||||||
(PAPER_TABLOID: Newspaper, 279.0, 432.0, "tabloid" | "ansi-b")
|
|
||||||
(PAPER_LEDGER: Base, 432.0, 279.0, "ledger")
|
|
||||||
(PAPER_JUNIOR_LEGAL: US, 127.0, 203.0, "junior-legal" | "index-card")
|
|
||||||
(PAPER_HALF_LETTER: Base, 140.0, 216.0, "half-letter")
|
|
||||||
(PAPER_GOVERNMENT_LETTER: US, 203.0, 267.0, "government-letter")
|
|
||||||
(PAPER_GOVERNMENT_LEGAL: US, 216.0, 330.0, "government-legal" | "officio")
|
|
||||||
|
|
||||||
// ANSI Extensions
|
|
||||||
(PAPER_ANSI_C: Base, 432.0, 559.0, "ansi-c")
|
|
||||||
(PAPER_ANSI_D: Base, 559.0, 864.0, "ansi-d")
|
|
||||||
(PAPER_ANSI_E: Base, 864.0, 1118.0, "ansi-e")
|
|
||||||
(PAPER_ENGINEERING_F: Base, 711.0, 1016.0, "engineering-f" | "engineering" |
|
|
||||||
"navfac" | "aerospace")
|
|
||||||
|
|
||||||
// Architectural Paper
|
|
||||||
(PAPER_ARCH_A: Base, 229.0, 305.0, "arch-a" | "arch-1")
|
|
||||||
(PAPER_ARCH_B: Base, 305.0, 457.0, "arch-b" | "arch-2" | "extra-tabloide")
|
|
||||||
(PAPER_ARCH_C: Base, 457.0, 610.0, "arch-c" | "arch-3")
|
|
||||||
(PAPER_ARCH_D: Base, 610.0, 914.0, "arch-d" | "arch-4")
|
|
||||||
(PAPER_ARCH_E1: Base, 762.0, 1067.0, "arch-e1" | "arch-5")
|
|
||||||
(PAPER_ARCH_E: Base, 914.0, 1219.0, "arch-e" | "arch-6")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// Japan
|
|
||||||
|
|
||||||
// JIS B Series
|
|
||||||
(PAPER_JIS_B0: Base, 1030.0, 1456.0, "jis-b0" | "jb0")
|
|
||||||
(PAPER_JIS_B1: Base, 728.0, 1030.0, "jis-b1" | "jb1")
|
|
||||||
(PAPER_JIS_B2: Base, 515.0, 728.0, "jis-b2" | "jb2")
|
|
||||||
(PAPER_JIS_B3: Base, 364.0, 515.0, "jis-b3" | "jb3")
|
|
||||||
(PAPER_JIS_B4: Base, 257.0, 364.0, "jis-b4" | "jb4")
|
|
||||||
(PAPER_JIS_B5: Base, 182.0, 257.0, "jis-b5" | "jb5")
|
|
||||||
(PAPER_JIS_B6: Base, 128.0, 182.0, "jis-b6" | "jb6")
|
|
||||||
(PAPER_JIS_B7: Base, 91.0, 128.0, "jis-b7" | "jb7")
|
|
||||||
(PAPER_JIS_B8: Base, 64.0, 91.0, "jis-b8" | "jb8")
|
|
||||||
(PAPER_JIS_B9: Base, 45.0, 64.0, "jis-b9" | "jb9")
|
|
||||||
(PAPER_JIS_B10: Base, 32.0, 45.0, "jis-b10" | "jb10")
|
|
||||||
(PAPER_JIS_B11: Base, 22.0, 32.0, "jis-b11" | "jb11")
|
|
||||||
|
|
||||||
// Traditional
|
|
||||||
(PAPER_SHIROKU_BAN_4: Base, 264.0, 379.0, "shiroku-ban-4")
|
|
||||||
(PAPER_SHIROKU_BAN_5: Base, 189.0, 262.0, "shiroku-ban-5")
|
|
||||||
(PAPER_SHIROKU_BAN_6: Base, 127.0, 188.0, "shiroku-ban-6")
|
|
||||||
(PAPER_KIKU_4: Base, 227.0, 306.0, "kiku-4")
|
|
||||||
(PAPER_KIKU_5: Base, 151.0, 227.0, "kiku-5")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// China
|
|
||||||
|
|
||||||
// Chinese D Series
|
|
||||||
(PAPER_SAC_D0: Base, 764.0, 1064.0, "sac-d0" | "cn-d0")
|
|
||||||
(PAPER_SAC_D1: Base, 532.0, 760.0, "sac-d1" | "cn-d1")
|
|
||||||
(PAPER_SAC_D2: Base, 380.0, 528.0, "sac-d2" | "cn-d2")
|
|
||||||
(PAPER_SAC_D3: Base, 264.0, 376.0, "sac-d3" | "cn-d3")
|
|
||||||
(PAPER_SAC_D4: Base, 188.0, 260.0, "sac-d4" | "cn-d4")
|
|
||||||
(PAPER_SAC_D5: Base, 130.0, 184.0, "sac-d5" | "cn-d5")
|
|
||||||
(PAPER_SAC_D6: Base, 92.0, 126.0, "sac-d6" | "cn-d6")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// United Kingdom Imperial (Assortment)
|
|
||||||
|
|
||||||
(PAPER_MONARCH: Base, 184.0, 267.0, "monarch")
|
|
||||||
(PAPER_QUARTO: Base, 229.0, 279.0, "quarto" | "us-quarto")
|
|
||||||
(PAPER_UK_QUARTO: Base, 203.0, 254.0, "uk-quarto" | "imperial-quarto")
|
|
||||||
(PAPER_UK_FOOLSCAP: Base, 343.0, 432.0, "foolscap" | "us-foolscap")
|
|
||||||
(PAPER_FOOLSCAP: Base, 203.0, 330.0, "imperial-foolscap" | "uk-foolscap")
|
|
||||||
(PAPER_POTT: Base, 318.0, 381.0, "pott")
|
|
||||||
(PAPER_CROWN: Base, 318.0, 508.0, "crown")
|
|
||||||
(PAPER_PINCHED_POST: Base, 375.0, 470.0, "pinched-post")
|
|
||||||
(PAPER_POST: Base, 394.0, 489.0, "post")
|
|
||||||
(PAPER_LARGE_POST: Base, 419.0, 533.0, "large-post")
|
|
||||||
(PAPER_DEMY: Base, 445.0, 572.0, "demy")
|
|
||||||
(PAPER_ROYAL: Base, 508.0, 635.0, "royal")
|
|
||||||
(PAPER_DOUBLE_CROWN: Base, 508.0, 762.0, "double-crown" | "theatre")
|
|
||||||
(PAPER_ELEPHANT: Base, 584.0, 711.0, "elephant")
|
|
||||||
(PAPER_DOUBLE_ROYAL: Base, 635.0, 1016.0, "double-royal" | "rail")
|
|
||||||
(PAPER_QUAD_CROWN: Base, 762.0, 1016.0, "quad-crown" | "cinema")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// French Traditional (AFNOR)
|
|
||||||
|
|
||||||
(PAPER_CLOCHE: Base, 300.0, 400.0, "cloche")
|
|
||||||
(PAPER_POT: Base, 310.0, 400.0, "pot" | "ecolier" | "écolier")
|
|
||||||
(PAPER_TELLIERE: Base, 340.0, 440.0, "telliere" | "tellière")
|
|
||||||
(PAPER_COURONNE_ECRITURE: Base, 360.0, 460.0, "couronne-ecriture" |
|
|
||||||
"couronne" | "couronne-écriture")
|
|
||||||
(PAPER_COURONNE_EDITION: Base, 370.0, 470.0, "couronne-edition" |
|
|
||||||
"couronne-édition")
|
|
||||||
(PAPER_ROBERTO: Base, 390.0, 500.0, "roberto")
|
|
||||||
(PAPER_ECU: Base, 400.0, 520.0, "ecu" | "écu")
|
|
||||||
(PAPER_COQUILLE: Base, 440.0, 560.0, "coquille")
|
|
||||||
(PAPER_CARRE: Base, 450.0, 560.0, "carre" | "carré")
|
|
||||||
(PAPER_CAVALIER: Base, 460.0, 620.0, "cavalier")
|
|
||||||
(PAPER_DEMI_RAISIN: Base, 325.0, 500.0, "demi-raisin")
|
|
||||||
(PAPER_RAISIN: Base, 500.0, 650.0, "raisin" | "dessin")
|
|
||||||
(PAPER_DOUBLE_RAISIN: Base, 650.0, 1000.0, "double-raisin")
|
|
||||||
(PAPER_JESUS: Base, 560.0, 760.0, "jesus" | "jésus")
|
|
||||||
(PAPER_SOLEIL: Base, 600.0, 800.0, "soleil")
|
|
||||||
(PAPER_COLOMBIER_AFFICHE: Base, 600.0, 800.0, "colombier-affiche" | "affiche")
|
|
||||||
(PAPER_COLOMBIER_COMMERCIAL: Base, 630.0, 900.0, "colombier-commercial")
|
|
||||||
(PAPER_PETIT_AIGLE: Base, 700.0, 940.0, "petit-aigle")
|
|
||||||
(PAPER_GRAND_AIGLE: Base, 750.0, 1060.0, "grand-aigle" | "napoleon")
|
|
||||||
(PAPER_GRAND_MONDE: Base, 900.0, 1260.0, "grand-monde")
|
|
||||||
(PAPER_UNIVERS: Base, 1000.0, 1300.0, "univers" | "universe")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// Newspaper
|
|
||||||
|
|
||||||
(PAPER_COMPACT: Newspaper, 280.0, 430.0, "compact")
|
|
||||||
(PAPER_BERLINER: Newspaper, 315.0, 470.0, "berliner" | "midi")
|
|
||||||
(PAPER_RHENISH: Newspaper, 350.0, 520.0, "rhenish")
|
|
||||||
(PAPER_BROADSHEET: Newspaper, 381.0, 578.0, "broadsheet" | "newspaper")
|
|
||||||
(PAPER_NEW_YORK_TIMES: Newspaper, 305.0, 559.0, "new-york-times" | "times")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// Books
|
|
||||||
|
|
||||||
(PAPER_FOLIO_BOOK: Book, 304.8, 482.6, "book-folio")
|
|
||||||
(PAPER_QUARTO_BOOK: Book, 241.3, 304.8, "book-quarto")
|
|
||||||
(PAPER_OCTAVO_BOOK: Book, 152.4, 228.6, "book-octavo")
|
|
||||||
(PAPER_16_MO_BOOK: Book, 101.6, 171.45, "book-16mo")
|
|
||||||
(PAPER_32_MO_BOOK: Book, 88.9, 139.7, "book-32mo")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------- //
|
|
||||||
// Various
|
|
||||||
|
|
||||||
(PAPER_ID_1: Base, 85.6, 53.98, "id-card" | "id-1" | "iso-7810-id-1" |
|
|
||||||
"eu-business-card" | "business-card")
|
|
||||||
(PAPER_US_BUSINESS_CARD: Base, 88.9, 50.8, "us-business-card")
|
|
||||||
(PAPER_JP_BUSINESS_CARD: Base, 91.0, 55.0, "jp-business-card")
|
|
||||||
(PAPER_CN_BUSINESS_CARD: Base, 90.0, 54.0, "cn-business-card")
|
|
||||||
(PAPER_A4_16_9: Base, 297.0, 167.0625, "presentation-16-9" | "presentation")
|
|
||||||
(PAPER_A4_4_3: Base, 280.0, 210.0, "presentation-4-3")
|
|
||||||
(PAPER_POSTCARD: Base, 152.4, 101.6, "postcard")
|
|
||||||
}
|
|
||||||
|
@ -6,13 +6,12 @@ use std::ops::Deref;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::u8;
|
use std::u8;
|
||||||
|
|
||||||
use crate::problem::Problems;
|
use crate::diagnostic::Diagnostics;
|
||||||
use crate::size::Size;
|
use crate::length::Length;
|
||||||
use super::func::{Key, Value};
|
use super::func::{Key, Value};
|
||||||
use super::span::{Span, Spanned};
|
use super::span::{Span, Spanned};
|
||||||
use super::tokens::is_identifier;
|
use super::tokens::is_identifier;
|
||||||
|
|
||||||
|
|
||||||
/// An argument or return value.
|
/// An argument or return value.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
@ -22,8 +21,8 @@ pub enum Expr {
|
|||||||
Str(String),
|
Str(String),
|
||||||
/// A number: `1.2, 200%`.
|
/// A number: `1.2, 200%`.
|
||||||
Number(f64),
|
Number(f64),
|
||||||
/// A size: `2cm, 5.2in`.
|
/// A length: `2cm, 5.2in`.
|
||||||
Size(Size),
|
Length(Length),
|
||||||
/// A bool: `true, false`.
|
/// A bool: `true, false`.
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
/// A color value, including the alpha channel: `#f79143ff`.
|
/// A color value, including the alpha channel: `#f79143ff`.
|
||||||
@ -32,7 +31,7 @@ pub enum Expr {
|
|||||||
Tuple(Tuple),
|
Tuple(Tuple),
|
||||||
/// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`.
|
/// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`.
|
||||||
NamedTuple(NamedTuple),
|
NamedTuple(NamedTuple),
|
||||||
/// An object: `{ fit: false, size: 12pt }`.
|
/// An object: `{ fit: false, width: 12pt }`.
|
||||||
Object(Object),
|
Object(Object),
|
||||||
/// An operator that negates the contained expression.
|
/// An operator that negates the contained expression.
|
||||||
Neg(Box<Spanned<Expr>>),
|
Neg(Box<Spanned<Expr>>),
|
||||||
@ -54,7 +53,7 @@ impl Expr {
|
|||||||
Ident(_) => "identifier",
|
Ident(_) => "identifier",
|
||||||
Str(_) => "string",
|
Str(_) => "string",
|
||||||
Number(_) => "number",
|
Number(_) => "number",
|
||||||
Size(_) => "size",
|
Length(_) => "length",
|
||||||
Bool(_) => "bool",
|
Bool(_) => "bool",
|
||||||
Color(_) => "color",
|
Color(_) => "color",
|
||||||
Tuple(_) => "tuple",
|
Tuple(_) => "tuple",
|
||||||
@ -76,7 +75,7 @@ impl Debug for Expr {
|
|||||||
Ident(i) => i.fmt(f),
|
Ident(i) => i.fmt(f),
|
||||||
Str(s) => s.fmt(f),
|
Str(s) => s.fmt(f),
|
||||||
Number(n) => n.fmt(f),
|
Number(n) => n.fmt(f),
|
||||||
Size(s) => s.fmt(f),
|
Length(s) => s.fmt(f),
|
||||||
Bool(b) => b.fmt(f),
|
Bool(b) => b.fmt(f),
|
||||||
Color(c) => c.fmt(f),
|
Color(c) => c.fmt(f),
|
||||||
Tuple(t) => t.fmt(f),
|
Tuple(t) => t.fmt(f),
|
||||||
@ -128,7 +127,7 @@ impl Debug for Ident {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// [box: background=#423abaff]
|
/// [page: background=#423abaff]
|
||||||
/// ^^^^^^^^
|
/// ^^^^^^^^
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -256,28 +255,28 @@ impl Tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract (and remove) the first matching value and remove and generate
|
/// Extract (and remove) the first matching value and remove and generate
|
||||||
/// problems for all previous items that did not match.
|
/// diagnostics for all previous items that did not match.
|
||||||
pub fn get<V: Value>(&mut self, problems: &mut Problems) -> Option<V> {
|
pub fn get<V: Value>(&mut self, diagnostics: &mut Diagnostics) -> Option<V> {
|
||||||
while !self.items.is_empty() {
|
while !self.items.is_empty() {
|
||||||
let expr = self.items.remove(0);
|
let expr = self.items.remove(0);
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => return Some(output),
|
Ok(output) => return Some(output),
|
||||||
Err(v) => problems.push(Spanned { v, span }),
|
Err(v) => diagnostics.push(Spanned { v, span }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract and return an iterator over all values that match and generate
|
/// Extract and return an iterator over all values that match and generate
|
||||||
/// problems for all items that do not match.
|
/// diagnostics for all items that do not match.
|
||||||
pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems)
|
pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics)
|
||||||
-> impl Iterator<Item=V> + 'a {
|
-> impl Iterator<Item=V> + 'a {
|
||||||
self.items.drain(..).filter_map(move |expr| {
|
self.items.drain(..).filter_map(move |expr| {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => Some(output),
|
Ok(output) => Some(output),
|
||||||
Err(v) => { problems.push(Spanned { v, span }); None }
|
Err(v) => { diagnostics.push(Spanned { v, span }); None }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -351,13 +350,9 @@ impl Deref for NamedTuple {
|
|||||||
|
|
||||||
/// A key-value collection of identifiers and associated expressions.
|
/// A key-value collection of identifiers and associated expressions.
|
||||||
///
|
///
|
||||||
/// The pairs themselves are not spanned, but the combined spans can easily be
|
|
||||||
/// retrieved by merging the spans of key and value as happening in
|
|
||||||
/// [`FuncArg::span`](super::func::FuncArg::span).
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// { fit: false, size: 12cm, items: (1, 2, 3) }
|
/// { fit: false, width: 12cm, items: (1, 2, 3) }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Default, Clone, PartialEq)]
|
#[derive(Default, Clone, PartialEq)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
@ -398,9 +393,9 @@ impl Object {
|
|||||||
///
|
///
|
||||||
/// Inserts an error if the value does not match. If the key is not
|
/// Inserts an error if the value does not match. If the key is not
|
||||||
/// contained, no error is inserted.
|
/// contained, no error is inserted.
|
||||||
pub fn get<V: Value>(&mut self, problems: &mut Problems, key: &str) -> Option<V> {
|
pub fn get<V: Value>(&mut self, diagnostics: &mut Diagnostics, key: &str) -> Option<V> {
|
||||||
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
|
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
|
||||||
self.get_index::<V>(problems, index)
|
self.get_index::<V>(diagnostics, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract (and remove) a pair with a matching key and value.
|
/// Extract (and remove) a pair with a matching key and value.
|
||||||
@ -409,12 +404,12 @@ impl Object {
|
|||||||
/// found, no error is inserted.
|
/// found, no error is inserted.
|
||||||
pub fn get_with_key<K: Key, V: Value>(
|
pub fn get_with_key<K: Key, V: Value>(
|
||||||
&mut self,
|
&mut self,
|
||||||
problems: &mut Problems,
|
diagnostics: &mut Diagnostics,
|
||||||
) -> Option<(K, V)> {
|
) -> Option<(K, V)> {
|
||||||
for (index, pair) in self.pairs.iter().enumerate() {
|
for (index, pair) in self.pairs.iter().enumerate() {
|
||||||
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
|
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
|
||||||
if let Some(key) = K::parse(key) {
|
if let Some(key) = K::parse(key) {
|
||||||
return self.get_index::<V>(problems, index).map(|value| (key, value));
|
return self.get_index::<V>(diagnostics, index).map(|value| (key, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -425,7 +420,7 @@ impl Object {
|
|||||||
/// Inserts errors for values that do not match.
|
/// Inserts errors for values that do not match.
|
||||||
pub fn get_all<'a, K: Key, V: Value>(
|
pub fn get_all<'a, K: Key, V: Value>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
problems: &'a mut Problems,
|
diagnostics: &'a mut Diagnostics,
|
||||||
) -> impl Iterator<Item=(K, V)> + 'a {
|
) -> impl Iterator<Item=(K, V)> + 'a {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
@ -434,7 +429,7 @@ impl Object {
|
|||||||
let key = Spanned { v: key.v.as_str(), span: key.span };
|
let key = Spanned { v: key.v.as_str(), span: key.span };
|
||||||
|
|
||||||
Some(if let Some(key) = K::parse(key) {
|
Some(if let Some(key) = K::parse(key) {
|
||||||
self.get_index::<V>(problems, index).map(|v| (key, v))
|
self.get_index::<V>(diagnostics, index).map(|v| (key, v))
|
||||||
} else {
|
} else {
|
||||||
index += 1;
|
index += 1;
|
||||||
None
|
None
|
||||||
@ -454,20 +449,20 @@ impl Object {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
|
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
problems: &'a mut Problems,
|
diagnostics: &'a mut Diagnostics,
|
||||||
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
|
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
|
||||||
self.get_all::<Spanned<K>, Spanned<V>>(problems)
|
self.get_all::<Spanned<K>, Spanned<V>>(diagnostics)
|
||||||
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span)))
|
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the argument at the given index and insert an error if the value
|
/// Extract the argument at the given index and insert an error if the value
|
||||||
/// does not match.
|
/// does not match.
|
||||||
fn get_index<V: Value>(&mut self, problems: &mut Problems, index: usize) -> Option<V> {
|
fn get_index<V: Value>(&mut self, diagnostics: &mut Diagnostics, index: usize) -> Option<V> {
|
||||||
let expr = self.pairs.remove(index).v.value;
|
let expr = self.pairs.remove(index).v.value;
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => Some(output),
|
Ok(output) => Some(output),
|
||||||
Err(v) => { problems.push(Spanned { v, span }); None }
|
Err(v) => { diagnostics.push(Spanned { v, span }); None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ use super::*;
|
|||||||
use self::AxisKey::*;
|
use self::AxisKey::*;
|
||||||
use self::PaddingKey::*;
|
use self::PaddingKey::*;
|
||||||
|
|
||||||
|
|
||||||
/// Key types are used to extract keyword arguments from
|
/// Key types are used to extract keyword arguments from
|
||||||
/// [`Objects`](crate::syntax::expr::Object). They represent the key part of a
|
/// [`Objects`](crate::syntax::expr::Object). They represent the key part of a
|
||||||
/// keyword argument.
|
/// keyword argument.
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
//! Deduplicating maps and keys for argument parsing.
|
//! Deduplicating maps and keys for argument parsing.
|
||||||
|
|
||||||
use crate::problem::Problems;
|
use crate::diagnostic::Diagnostics;
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
use crate::size::{PSize, ValueBox};
|
use crate::length::{ScaleLength, Value4};
|
||||||
use crate::syntax::span::Spanned;
|
use crate::syntax::span::Spanned;
|
||||||
use super::keys::*;
|
use super::keys::*;
|
||||||
use super::values::*;
|
use super::values::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// A map which deduplicates redundant arguments.
|
/// A map which deduplicates redundant arguments.
|
||||||
///
|
///
|
||||||
/// Whenever a duplicate argument is inserted into the map, through the
|
/// Whenever a duplicate argument is inserted into the map, through the
|
||||||
/// functions `from_iter`, `insert` or `extend` an problems is added to the error
|
/// functions `from_iter`, `insert` or `extend` an diagnostics is added to the error
|
||||||
/// list that needs to be passed to those functions.
|
/// list that needs to be passed to those functions.
|
||||||
///
|
///
|
||||||
/// All entries need to have span information to enable the error reporting.
|
/// All entries need to have span information to enable the error reporting.
|
||||||
@ -28,27 +27,27 @@ impl<K, V> DedupMap<K, V> where K: Eq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new map from an iterator of spanned keys and values.
|
/// Create a new map from an iterator of spanned keys and values.
|
||||||
pub fn from_iter<I>(problems: &mut Problems, iter: I) -> DedupMap<K, V>
|
pub fn from_iter<I>(diagnostics: &mut Diagnostics, iter: I) -> DedupMap<K, V>
|
||||||
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
map.extend(problems, iter);
|
map.extend(diagnostics, iter);
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a spanned key-value pair.
|
/// Add a spanned key-value pair.
|
||||||
pub fn insert(&mut self, problems: &mut Problems, entry: Spanned<(K, V)>) {
|
pub fn insert(&mut self, diagnostics: &mut Diagnostics, entry: Spanned<(K, V)>) {
|
||||||
if self.map.iter().any(|e| e.v.0 == entry.v.0) {
|
if self.map.iter().any(|e| e.v.0 == entry.v.0) {
|
||||||
problems.push(error!(entry.span, "duplicate argument"));
|
diagnostics.push(error!(entry.span, "duplicate argument"));
|
||||||
} else {
|
} else {
|
||||||
self.map.push(entry);
|
self.map.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple spanned key-value pairs.
|
/// Add multiple spanned key-value pairs.
|
||||||
pub fn extend<I>(&mut self, problems: &mut Problems, items: I)
|
pub fn extend<I>(&mut self, diagnostics: &mut Diagnostics, items: I)
|
||||||
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
||||||
for item in items.into_iter() {
|
for item in items.into_iter() {
|
||||||
self.insert(problems, item);
|
self.insert(diagnostics, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,15 +70,15 @@ impl<K, V> DedupMap<K, V> where K: Eq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new map where keys and values are mapped to new keys and
|
/// Create a new map where keys and values are mapped to new keys and
|
||||||
/// values. When the mapping introduces new duplicates, problems are
|
/// values. When the mapping introduces new duplicates, diagnostics are
|
||||||
/// generated.
|
/// generated.
|
||||||
pub fn dedup<F, K2, V2>(&self, problems: &mut Problems, mut f: F) -> DedupMap<K2, V2>
|
pub fn dedup<F, K2, V2>(&self, diagnostics: &mut Diagnostics, mut f: F) -> DedupMap<K2, V2>
|
||||||
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
|
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
for Spanned { v: (key, value), span } in self.map.iter() {
|
for Spanned { v: (key, value), span } in self.map.iter() {
|
||||||
let (key, value) = f(key, value);
|
let (key, value) = f(key, value);
|
||||||
map.insert(problems, Spanned { v: (key, value), span: *span });
|
map.insert(diagnostics, Spanned { v: (key, value), span: *span });
|
||||||
}
|
}
|
||||||
|
|
||||||
map
|
map
|
||||||
@ -98,21 +97,21 @@ pub struct AxisMap<V>(DedupMap<AxisKey, V>);
|
|||||||
impl<V: Value> AxisMap<V> {
|
impl<V: Value> AxisMap<V> {
|
||||||
/// Parse an axis map from the object.
|
/// Parse an axis map from the object.
|
||||||
pub fn parse<K>(
|
pub fn parse<K>(
|
||||||
problems: &mut Problems,
|
diagnostics: &mut Diagnostics,
|
||||||
object: &mut Object,
|
object: &mut Object,
|
||||||
) -> AxisMap<V> where K: Key + Into<AxisKey> {
|
) -> AxisMap<V> where K: Key + Into<AxisKey> {
|
||||||
let values: Vec<_> = object
|
let values: Vec<_> = object
|
||||||
.get_all_spanned::<K, V>(problems)
|
.get_all_spanned::<K, V>(diagnostics)
|
||||||
.map(|s| s.map(|(k, v)| (k.into(), v)))
|
.map(|s| s.map(|(k, v)| (k.into(), v)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
AxisMap(DedupMap::from_iter(problems, values))
|
AxisMap(DedupMap::from_iter(diagnostics, values))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deduplicate from specific or generic to just specific axes.
|
/// Deduplicate from specific or generic to just specific axes.
|
||||||
pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
|
pub fn dedup(&self, diagnostics: &mut Diagnostics, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
|
||||||
where V: Clone {
|
where V: Clone {
|
||||||
self.0.dedup(problems, |key, val| (key.to_specific(axes), val.clone()))
|
self.0.dedup(diagnostics, |key, val| (key.to_specific(axes), val.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,23 +123,23 @@ pub struct PosAxisMap<V>(DedupMap<PosAxisKey, V>);
|
|||||||
impl<V: Value> PosAxisMap<V> {
|
impl<V: Value> PosAxisMap<V> {
|
||||||
/// Parse a positional/axis map from the function arguments.
|
/// Parse a positional/axis map from the function arguments.
|
||||||
pub fn parse<K>(
|
pub fn parse<K>(
|
||||||
problems: &mut Problems,
|
diagnostics: &mut Diagnostics,
|
||||||
args: &mut FuncArgs,
|
args: &mut FuncArgs,
|
||||||
) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
|
) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
for &key in &[PosAxisKey::First, PosAxisKey::Second] {
|
for &key in &[PosAxisKey::First, PosAxisKey::Second] {
|
||||||
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(problems) {
|
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(diagnostics) {
|
||||||
map.insert(problems, Spanned { v: (key, v), span })
|
map.insert(diagnostics, Spanned { v: (key, v), span })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let keywords: Vec<_> = args.key
|
let keywords: Vec<_> = args.key
|
||||||
.get_all_spanned::<K, V>(problems)
|
.get_all_spanned::<K, V>(diagnostics)
|
||||||
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
|
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
map.extend(problems, keywords);
|
map.extend(diagnostics, keywords);
|
||||||
|
|
||||||
PosAxisMap(map)
|
PosAxisMap(map)
|
||||||
}
|
}
|
||||||
@ -149,7 +148,7 @@ impl<V: Value> PosAxisMap<V> {
|
|||||||
/// or specific axes to just generic axes.
|
/// or specific axes to just generic axes.
|
||||||
pub fn dedup<F>(
|
pub fn dedup<F>(
|
||||||
&self,
|
&self,
|
||||||
problems: &mut Problems,
|
diagnostics: &mut Diagnostics,
|
||||||
axes: LayoutAxes,
|
axes: LayoutAxes,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> DedupMap<GenericAxis, V>
|
) -> DedupMap<GenericAxis, V>
|
||||||
@ -157,7 +156,7 @@ impl<V: Value> PosAxisMap<V> {
|
|||||||
F: FnMut(&V) -> Option<GenericAxis>,
|
F: FnMut(&V) -> Option<GenericAxis>,
|
||||||
V: Clone,
|
V: Clone,
|
||||||
{
|
{
|
||||||
self.0.dedup(problems, |key, val| {
|
self.0.dedup(diagnostics, |key, val| {
|
||||||
(match key {
|
(match key {
|
||||||
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
|
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
|
||||||
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
|
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
|
||||||
@ -171,24 +170,24 @@ impl<V: Value> PosAxisMap<V> {
|
|||||||
/// A map for storing padding given for a combination of all sides, opposing
|
/// A map for storing padding given for a combination of all sides, opposing
|
||||||
/// sides or single sides.
|
/// sides or single sides.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>);
|
pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<ScaleLength>>);
|
||||||
|
|
||||||
impl PaddingMap {
|
impl PaddingMap {
|
||||||
/// Parse a padding map from the function arguments.
|
/// Parse a padding map from the function arguments.
|
||||||
pub fn parse(problems: &mut Problems, args: &mut FuncArgs) -> PaddingMap {
|
pub fn parse(diagnostics: &mut Diagnostics, args: &mut FuncArgs) -> PaddingMap {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
let all = args.pos.get::<Spanned<Defaultable<PSize>>>(problems);
|
let all = args.pos.get::<Spanned<Defaultable<ScaleLength>>>(diagnostics);
|
||||||
if let Some(Spanned { v, span }) = all {
|
if let Some(Spanned { v, span }) = all {
|
||||||
map.insert(problems, Spanned { v: (PaddingKey::All, v.into()), span });
|
map.insert(diagnostics, Spanned { v: (PaddingKey::All, v.into()), span });
|
||||||
}
|
}
|
||||||
|
|
||||||
let paddings: Vec<_> = args.key
|
let paddings: Vec<_> = args.key
|
||||||
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(problems)
|
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<ScaleLength>>(diagnostics)
|
||||||
.map(|s| s.map(|(k, v)| (k, v.into())))
|
.map(|s| s.map(|(k, v)| (k, v.into())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
map.extend(problems, paddings);
|
map.extend(diagnostics, paddings);
|
||||||
|
|
||||||
PaddingMap(map)
|
PaddingMap(map)
|
||||||
}
|
}
|
||||||
@ -196,13 +195,13 @@ impl PaddingMap {
|
|||||||
/// Apply the specified padding on a value box of optional, scalable sizes.
|
/// Apply the specified padding on a value box of optional, scalable sizes.
|
||||||
pub fn apply(
|
pub fn apply(
|
||||||
&self,
|
&self,
|
||||||
problems: &mut Problems,
|
diagnostics: &mut Diagnostics,
|
||||||
axes: LayoutAxes,
|
axes: LayoutAxes,
|
||||||
padding: &mut ValueBox<Option<PSize>>
|
padding: &mut Value4<Option<ScaleLength>>
|
||||||
) {
|
) {
|
||||||
use PaddingKey::*;
|
use PaddingKey::*;
|
||||||
|
|
||||||
let map = self.0.dedup(problems, |key, &val| {
|
let map = self.0.dedup(diagnostics, |key, &val| {
|
||||||
(match key {
|
(match key {
|
||||||
All => All,
|
All => All,
|
||||||
Both(axis) => Both(axis.to_specific(axes)),
|
Both(axis) => Both(axis.to_specific(axes)),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Primitives for argument parsing in library functions.
|
//! Primitives for argument parsing in library functions.
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use crate::problem::{Problem, Problems};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use super::expr::{Expr, Ident, Tuple, Object, Pair};
|
use super::expr::{Expr, Ident, Tuple, Object, Pair};
|
||||||
use super::span::{Span, Spanned};
|
use super::span::{Span, Spanned};
|
||||||
|
|
||||||
@ -85,13 +85,13 @@ pub enum FuncArg {
|
|||||||
pub trait OptionExt: Sized {
|
pub trait OptionExt: Sized {
|
||||||
/// Add an error about a missing argument `arg` with the given span if the
|
/// Add an error about a missing argument `arg` with the given span if the
|
||||||
/// option is `None`.
|
/// option is `None`.
|
||||||
fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self;
|
fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionExt for Option<T> {
|
impl<T> OptionExt for Option<T> {
|
||||||
fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self {
|
fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
problems.push(error!(span, "missing argument: {}", arg));
|
diagnostics.push(error!(span, "missing argument: {}", arg));
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,12 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use toddle::query::{FontStyle, FontWeight};
|
use toddle::query::{FontStyle, FontWeight};
|
||||||
|
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
use crate::size::{Size, ScaleSize};
|
use crate::length::{Length, ScaleLength};
|
||||||
use crate::style::Paper;
|
use crate::paper::Paper;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use self::AlignmentValue::*;
|
use self::AlignmentValue::*;
|
||||||
|
|
||||||
|
|
||||||
/// Value types are used to extract the values of positional and keyword
|
/// Value types are used to extract the values of positional and keyword
|
||||||
/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and
|
/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and
|
||||||
/// [`Objects`](crate::syntax::expr::Object). They represent the value part of
|
/// [`Objects`](crate::syntax::expr::Object). They represent the value part of
|
||||||
@ -24,14 +23,14 @@ use self::AlignmentValue::*;
|
|||||||
/// An implementation for `bool` might look as follows:
|
/// An implementation for `bool` might look as follows:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use typstc::error;
|
/// # use typstc::error;
|
||||||
/// # use typstc::problem::Problem;
|
/// # use typstc::diagnostic::Diagnostic;
|
||||||
/// # use typstc::syntax::expr::Expr;
|
/// # use typstc::syntax::expr::Expr;
|
||||||
/// # use typstc::syntax::func::Value;
|
/// # use typstc::syntax::func::Value;
|
||||||
/// # use typstc::syntax::span::Spanned;
|
/// # use typstc::syntax::span::Spanned;
|
||||||
/// # struct Bool; /*
|
/// # struct Bool; /*
|
||||||
/// impl Value for bool {
|
/// impl Value for bool {
|
||||||
/// # */ impl Value for Bool {
|
/// # */ impl Value for Bool {
|
||||||
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
/// match expr.v {
|
/// match expr.v {
|
||||||
/// # /*
|
/// # /*
|
||||||
/// Expr::Bool(b) => Ok(b),
|
/// Expr::Bool(b) => Ok(b),
|
||||||
@ -44,11 +43,11 @@ use self::AlignmentValue::*;
|
|||||||
pub trait Value: Sized {
|
pub trait Value: Sized {
|
||||||
/// Parse an expression into this value or return an error if the expression
|
/// Parse an expression into this value or return an error if the expression
|
||||||
/// is valid for this value type.
|
/// is valid for this value type.
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem>;
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value> Value for Spanned<V> {
|
impl<V: Value> Value for Spanned<V> {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
V::parse(expr).map(|v| Spanned { v, span })
|
V::parse(expr).map(|v| Spanned { v, span })
|
||||||
}
|
}
|
||||||
@ -58,7 +57,7 @@ impl<V: Value> Value for Spanned<V> {
|
|||||||
macro_rules! value {
|
macro_rules! value {
|
||||||
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
||||||
impl Value for $type {
|
impl Value for $type {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
match expr.v {
|
match expr.v {
|
||||||
$($p => Ok($r)),*,
|
$($p => Ok($r)),*,
|
||||||
@ -77,13 +76,13 @@ value!(Ident, "identifier", Expr::Ident(i) => i);
|
|||||||
value!(String, "string", Expr::Str(s) => s);
|
value!(String, "string", Expr::Str(s) => s);
|
||||||
value!(f64, "number", Expr::Number(n) => n);
|
value!(f64, "number", Expr::Number(n) => n);
|
||||||
value!(bool, "bool", Expr::Bool(b) => b);
|
value!(bool, "bool", Expr::Bool(b) => b);
|
||||||
value!(Size, "size", Expr::Size(s) => s);
|
value!(Length, "length", Expr::Length(s) => s);
|
||||||
value!(Tuple, "tuple", Expr::Tuple(t) => t);
|
value!(Tuple, "tuple", Expr::Tuple(t) => t);
|
||||||
value!(Object, "object", Expr::Object(o) => o);
|
value!(Object, "object", Expr::Object(o) => o);
|
||||||
|
|
||||||
value!(ScaleSize, "number or size",
|
value!(ScaleLength, "number or length",
|
||||||
Expr::Size(size) => ScaleSize::Absolute(size),
|
Expr::Length(length) => ScaleLength::Absolute(length),
|
||||||
Expr::Number(scale) => ScaleSize::Scaled(scale as f32),
|
Expr::Number(scale) => ScaleLength::Scaled(scale as f64),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements
|
/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements
|
||||||
@ -108,20 +107,20 @@ impl From<StringLike> for String {
|
|||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use typstc::syntax::func::{FuncArgs, Defaultable};
|
/// # use typstc::syntax::func::{FuncArgs, Defaultable};
|
||||||
/// # use typstc::size::Size;
|
/// # use typstc::length::Length;
|
||||||
/// # let mut args = FuncArgs::new();
|
/// # let mut args = FuncArgs::new();
|
||||||
/// # let mut errors = vec![];
|
/// # let mut errors = vec![];
|
||||||
/// args.key.get::<Defaultable<Size>>(&mut errors, "size");
|
/// args.key.get::<Defaultable<Length>>(&mut errors, "length");
|
||||||
/// ```
|
/// ```
|
||||||
/// This will yield.
|
/// This will yield.
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// [func: size=default] => None
|
/// [func: length=default] => None
|
||||||
/// [func: size=2cm] => Some(Size::cm(2.0))
|
/// [func: length=2cm] => Some(Length::cm(2.0))
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Defaultable<V>(pub Option<V>);
|
pub struct Defaultable<V>(pub Option<V>);
|
||||||
|
|
||||||
impl<V: Value> Value for Defaultable<V> {
|
impl<V: Value> Value for Defaultable<V> {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
Ok(Defaultable(match expr.v {
|
Ok(Defaultable(match expr.v {
|
||||||
Expr::Ident(ident) if ident.as_str() == "default" => None,
|
Expr::Ident(ident) if ident.as_str() == "default" => None,
|
||||||
_ => Some(V::parse(expr)?)
|
_ => Some(V::parse(expr)?)
|
||||||
@ -136,7 +135,7 @@ impl<V> From<Defaultable<V>> for Option<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value for FontStyle {
|
impl Value for FontStyle {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
FontStyle::from_name(Ident::parse(expr)?.as_str())
|
FontStyle::from_name(Ident::parse(expr)?.as_str())
|
||||||
.ok_or_else(|| error!("invalid font style"))
|
.ok_or_else(|| error!("invalid font style"))
|
||||||
}
|
}
|
||||||
@ -145,7 +144,7 @@ impl Value for FontStyle {
|
|||||||
/// The additional boolean specifies whether a number was clamped into the range
|
/// The additional boolean specifies whether a number was clamped into the range
|
||||||
/// 100 - 900 to make it a valid font weight.
|
/// 100 - 900 to make it a valid font weight.
|
||||||
impl Value for (FontWeight, bool) {
|
impl Value for (FontWeight, bool) {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
match expr.v {
|
match expr.v {
|
||||||
Expr::Number(weight) => {
|
Expr::Number(weight) => {
|
||||||
let weight = weight.round();
|
let weight = weight.round();
|
||||||
@ -170,14 +169,14 @@ impl Value for (FontWeight, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value for Paper {
|
impl Value for Paper {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
Paper::from_name(Ident::parse(expr)?.as_str())
|
Paper::from_name(Ident::parse(expr)?.as_str())
|
||||||
.ok_or_else(|| error!("invalid paper type"))
|
.ok_or_else(|| error!("invalid paper type"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for Direction {
|
impl Value for Direction {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
Ok(match Ident::parse(expr)?.as_str() {
|
Ok(match Ident::parse(expr)?.as_str() {
|
||||||
"left-to-right" | "ltr" | "LTR" => LeftToRight,
|
"left-to-right" | "ltr" | "LTR" => LeftToRight,
|
||||||
"right-to-left" | "rtl" | "RTL" => RightToLeft,
|
"right-to-left" | "rtl" | "RTL" => RightToLeft,
|
||||||
@ -250,7 +249,7 @@ impl AlignmentValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value for AlignmentValue {
|
impl Value for AlignmentValue {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||||
Ok(match Ident::parse(expr)?.as_str() {
|
Ok(match Ident::parse(expr)?.as_str() {
|
||||||
"origin" => Align(Origin),
|
"origin" => Align(Origin),
|
||||||
"center" => Align(Center),
|
"center" => Align(Center),
|
||||||
|
@ -20,7 +20,6 @@ pub_use_mod!(scope);
|
|||||||
pub_use_mod!(parsing);
|
pub_use_mod!(parsing);
|
||||||
pub_use_mod!(tokens);
|
pub_use_mod!(tokens);
|
||||||
|
|
||||||
|
|
||||||
/// Represents a parsed piece of source that can be layouted and in the future
|
/// Represents a parsed piece of source that can be layouted and in the future
|
||||||
/// also be queried for information used for refactorings, autocomplete, etc.
|
/// also be queried for information used for refactorings, autocomplete, etc.
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
@ -94,6 +93,9 @@ impl PartialEq for Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of spanned decorations.
|
||||||
|
pub type Decorations = SpanVec<Decoration>;
|
||||||
|
|
||||||
/// Decorations for semantic syntax highlighting.
|
/// Decorations for semantic syntax highlighting.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -110,7 +112,6 @@ pub enum Decoration {
|
|||||||
/// ^^^^^^
|
/// ^^^^^^
|
||||||
/// ```
|
/// ```
|
||||||
InvalidFuncName,
|
InvalidFuncName,
|
||||||
|
|
||||||
/// A key of a keyword argument:
|
/// A key of a keyword argument:
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// [box: width=5cm]
|
/// [box: width=5cm]
|
||||||
@ -123,7 +124,6 @@ pub enum Decoration {
|
|||||||
/// ^^^^ ^^^^^
|
/// ^^^^ ^^^^^
|
||||||
/// ```
|
/// ```
|
||||||
ObjectKey,
|
ObjectKey,
|
||||||
|
|
||||||
/// An italic word.
|
/// An italic word.
|
||||||
Italic,
|
Italic,
|
||||||
/// A bold word.
|
/// A bold word.
|
||||||
|
@ -4,9 +4,12 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use super::expr::*;
|
use super::expr::*;
|
||||||
use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg};
|
use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg};
|
||||||
use super::span::{Position, Span, Spanned};
|
use super::span::{Pos, Span, Spanned};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// A function which parses a function call into a model.
|
||||||
|
pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
|
||||||
|
|
||||||
/// The state which can influence how a string of source code is parsed.
|
/// The state which can influence how a string of source code is parsed.
|
||||||
///
|
///
|
||||||
/// Parsing is pure - when passed in the same state and source code, the output
|
/// Parsing is pure - when passed in the same state and source code, the output
|
||||||
@ -22,7 +25,7 @@ pub struct ParseState {
|
|||||||
/// `offset` position. This is used to make spans of a function body relative to
|
/// `offset` position. This is used to make spans of a function body relative to
|
||||||
/// the start of the function as a whole as opposed to the start of the
|
/// the start of the function as a whole as opposed to the start of the
|
||||||
/// function's body.
|
/// function's body.
|
||||||
pub fn parse(src: &str, offset: Position, state: &ParseState) -> Pass<SyntaxModel> {
|
pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
|
||||||
let mut model = SyntaxModel::new();
|
let mut model = SyntaxModel::new();
|
||||||
let mut feedback = Feedback::new();
|
let mut feedback = Feedback::new();
|
||||||
|
|
||||||
@ -102,7 +105,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
state,
|
state,
|
||||||
// Start at column 1 because the opening bracket is also part of
|
// Start at column 1 because the opening bracket is also part of
|
||||||
// the function, but not part of the `header` string.
|
// the function, but not part of the `header` string.
|
||||||
tokens: Tokens::new(header, Position::new(0, 1), TokenMode::Header),
|
tokens: Tokens::new(header, Pos::new(0, 1), TokenMode::Header),
|
||||||
peeked: None,
|
peeked: None,
|
||||||
body,
|
body,
|
||||||
feedback: Feedback::new(),
|
feedback: Feedback::new(),
|
||||||
@ -127,7 +130,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.feedback.decos.push(Spanned::new(deco, header.name.span));
|
self.feedback.decorations.push(Spanned::new(deco, header.name.span));
|
||||||
(parser, header)
|
(parser, header)
|
||||||
} else {
|
} else {
|
||||||
// Parse the body with the fallback parser even when the header is
|
// Parse the body with the fallback parser even when the header is
|
||||||
@ -186,7 +189,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.skip_white();
|
self.skip_white();
|
||||||
|
|
||||||
let key = ident;
|
let key = ident;
|
||||||
self.feedback.decos.push(
|
self.feedback.decorations.push(
|
||||||
Spanned::new(Decoration::ArgumentKey, key.span)
|
Spanned::new(Decoration::ArgumentKey, key.span)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -325,7 +328,7 @@ impl FuncParser<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token::ExprNumber(n) => self.eat_span(Expr::Number(n)),
|
Token::ExprNumber(n) => self.eat_span(Expr::Number(n)),
|
||||||
Token::ExprSize(s) => self.eat_span(Expr::Size(s)),
|
Token::ExprLength(s) => self.eat_span(Expr::Length(s)),
|
||||||
Token::ExprBool(b) => self.eat_span(Expr::Bool(b)),
|
Token::ExprBool(b) => self.eat_span(Expr::Bool(b)),
|
||||||
Token::ExprHex(s) => {
|
Token::ExprHex(s) => {
|
||||||
if let Ok(color) = RgbaColor::from_str(s) {
|
if let Ok(color) = RgbaColor::from_str(s) {
|
||||||
@ -423,7 +426,7 @@ impl FuncParser<'_> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.feedback.decos.push(
|
self.feedback.decorations.push(
|
||||||
Spanned::new(Decoration::ObjectKey, key.span)
|
Spanned::new(Decoration::ObjectKey, key.span)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -464,7 +467,7 @@ impl FuncParser<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_at(&mut self, token: Token<'_>, pos: Position) -> bool {
|
fn expect_at(&mut self, token: Token<'_>, pos: Pos) -> bool {
|
||||||
if self.check(token) {
|
if self.check(token) {
|
||||||
self.eat();
|
self.eat();
|
||||||
true
|
true
|
||||||
@ -485,11 +488,11 @@ impl FuncParser<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_at(&mut self, thing: &str, pos: Position) {
|
fn expected_at(&mut self, thing: &str, pos: Pos) {
|
||||||
error!(@self.feedback, Span::at(pos), "expected {}", thing);
|
error!(@self.feedback, Span::at(pos), "expected {}", thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_found_or_at(&mut self, thing: &str, pos: Position) {
|
fn expected_found_or_at(&mut self, thing: &str, pos: Pos) {
|
||||||
if self.eof() {
|
if self.eof() {
|
||||||
self.expected_at(thing, pos)
|
self.expected_at(thing, pos)
|
||||||
} else {
|
} else {
|
||||||
@ -544,7 +547,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.peek().is_none()
|
self.peek().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pos(&self) -> Position {
|
fn pos(&self) -> Pos {
|
||||||
self.peeked.flatten()
|
self.peeked.flatten()
|
||||||
.map(|s| s.span.start)
|
.map(|s| s.span.start)
|
||||||
.unwrap_or_else(|| self.tokens.pos())
|
.unwrap_or_else(|| self.tokens.pos())
|
||||||
@ -604,13 +607,13 @@ fn unescape_raw(raw: &str) -> Vec<String> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::size::Size;
|
use crate::length::Length;
|
||||||
use super::super::test::{check, DebugFn};
|
use super::super::test::{check, DebugFn};
|
||||||
use super::super::func::Value;
|
use super::super::func::Value;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use Decoration::*;
|
use Decoration::*;
|
||||||
use Expr::{Number as Num, Size as Sz, Bool};
|
use Expr::{Number as Num, Length as Len, Bool};
|
||||||
use Node::{
|
use Node::{
|
||||||
Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
|
Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
|
||||||
Parbreak, Linebreak,
|
Parbreak, Linebreak,
|
||||||
@ -625,7 +628,7 @@ mod tests {
|
|||||||
p!($source => [$($model)*], []);
|
p!($source => [$($model)*], []);
|
||||||
};
|
};
|
||||||
|
|
||||||
($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
|
($source:expr => [$($model:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
|
||||||
let mut scope = Scope::new::<DebugFn>();
|
let mut scope = Scope::new::<DebugFn>();
|
||||||
scope.add::<DebugFn>("f");
|
scope.add::<DebugFn>("f");
|
||||||
scope.add::<DebugFn>("n");
|
scope.add::<DebugFn>("n");
|
||||||
@ -633,25 +636,25 @@ mod tests {
|
|||||||
scope.add::<DebugFn>("val");
|
scope.add::<DebugFn>("val");
|
||||||
|
|
||||||
let state = ParseState { scope };
|
let state = ParseState { scope };
|
||||||
let pass = parse($source, Position::ZERO, &state);
|
let pass = parse($source, Pos::ZERO, &state);
|
||||||
|
|
||||||
// Test model.
|
// Test model.
|
||||||
let (exp, cmp) = span_vec![$($model)*];
|
let (exp, cmp) = span_vec![$($model)*];
|
||||||
check($source, exp, pass.output.nodes, cmp);
|
check($source, exp, pass.output.nodes, cmp);
|
||||||
|
|
||||||
// Test problems.
|
// Test diagnostics.
|
||||||
let (exp, cmp) = span_vec![$($problems)*];
|
let (exp, cmp) = span_vec![$($diagnostics)*];
|
||||||
let exp = exp.into_iter()
|
let exp = exp.into_iter()
|
||||||
.map(|s: Spanned<&str>| s.map(|e| e.to_string()))
|
.map(|s: Spanned<&str>| s.map(|e| e.to_string()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let found = pass.feedback.problems.into_iter()
|
let found = pass.feedback.diagnostics.into_iter()
|
||||||
.map(|s| s.map(|e| e.message))
|
.map(|s| s.map(|e| e.message))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
check($source, exp, found, cmp);
|
check($source, exp, found, cmp);
|
||||||
|
|
||||||
// Test decos.
|
// Test decos.
|
||||||
$(let (exp, cmp) = span_vec![$($decos)*];
|
$(let (exp, cmp) = span_vec![$($decos)*];
|
||||||
check($source, exp, pass.feedback.decos, cmp);)?
|
check($source, exp, pass.feedback.decorations, cmp);)?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +667,6 @@ mod tests {
|
|||||||
|
|
||||||
fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
|
fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
|
||||||
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
||||||
fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) }
|
|
||||||
fn Color(r: u8, g: u8, b: u8, a: u8) -> Expr { Expr::Color(RgbaColor::new(r, g, b, a)) }
|
fn Color(r: u8, g: u8, b: u8, a: u8) -> Expr { Expr::Color(RgbaColor::new(r, g, b, a)) }
|
||||||
fn ColorStr(color: &str) -> Expr { Expr::Color(RgbaColor::from_str(color).expect("invalid test color")) }
|
fn ColorStr(color: &str) -> Expr { Expr::Color(RgbaColor::from_str(color).expect("invalid test color")) }
|
||||||
fn ColorHealed() -> Expr { Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)) }
|
fn ColorHealed() -> Expr { Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)) }
|
||||||
@ -878,8 +880,8 @@ mod tests {
|
|||||||
pval!("name" => (Id("name")));
|
pval!("name" => (Id("name")));
|
||||||
pval!("\"hi\"" => (Str("hi")));
|
pval!("\"hi\"" => (Str("hi")));
|
||||||
pval!("3.14" => (Num(3.14)));
|
pval!("3.14" => (Num(3.14)));
|
||||||
pval!("4.5cm" => (Sz(Size::cm(4.5))));
|
pval!("4.5cm" => (Len(Length::cm(4.5))));
|
||||||
pval!("12e1pt" => (Pt(12e1)));
|
pval!("12e1pt" => (Len(Length::pt(12e1))));
|
||||||
pval!("#f7a20500" => (ColorStr("f7a20500")));
|
pval!("#f7a20500" => (ColorStr("f7a20500")));
|
||||||
pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string")));
|
pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string")));
|
||||||
|
|
||||||
@ -890,10 +892,10 @@ mod tests {
|
|||||||
pval!("(hi)" => (Id("hi")));
|
pval!("(hi)" => (Id("hi")));
|
||||||
|
|
||||||
// Math.
|
// Math.
|
||||||
pval!("3.2in + 6pt" => (Add(Sz(Size::inches(3.2)), Sz(Size::pt(6.0)))));
|
pval!("3.2in + 6pt" => (Add(Len(Length::inches(3.2)), Len(Length::pt(6.0)))));
|
||||||
pval!("5 - 0.01" => (Sub(Num(5.0), Num(0.01))));
|
pval!("5 - 0.01" => (Sub(Num(5.0), Num(0.01))));
|
||||||
pval!("(3mm * 2)" => (Mul(Sz(Size::mm(3.0)), Num(2.0))));
|
pval!("(3mm * 2)" => (Mul(Len(Length::mm(3.0)), Num(2.0))));
|
||||||
pval!("12e-3cm/1pt" => (Div(Sz(Size::cm(12e-3)), Sz(Size::pt(1.0)))));
|
pval!("12e-3cm/1pt" => (Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0)))));
|
||||||
|
|
||||||
// Unclosed string.
|
// Unclosed string.
|
||||||
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
|
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
|
||||||
@ -912,26 +914,24 @@ mod tests {
|
|||||||
fn parse_complex_mathematical_expressions() {
|
fn parse_complex_mathematical_expressions() {
|
||||||
// Valid expressions.
|
// Valid expressions.
|
||||||
pval!("(3.2in + 6pt)*(5/2-1)" => (Mul(
|
pval!("(3.2in + 6pt)*(5/2-1)" => (Mul(
|
||||||
Add(Sz(Size::inches(3.2)), Sz(Size::pt(6.0))),
|
Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))),
|
||||||
Sub(Div(Num(5.0), Num(2.0)), Num(1.0))
|
Sub(Div(Num(5.0), Num(2.0)), Num(1.0))
|
||||||
)));
|
)));
|
||||||
pval!("(6.3E+2+4* - 3.2pt)/2" => (Div(
|
pval!("(6.3E+2+4* - 3.2pt)/2" => (Div(
|
||||||
Add(Num(6.3e2),Mul(Num(4.0), Neg(Pt(3.2)))),
|
Add(Num(6.3e2), Mul(Num(4.0), Neg(Len(Length::pt(3.2))))),
|
||||||
Num(2.0)
|
Num(2.0)
|
||||||
)));
|
)));
|
||||||
|
|
||||||
// Associativity of multiplication and division.
|
// Associativity of multiplication and division.
|
||||||
p!("[val: 3/4*5]" =>
|
pval!("3/4*5" => (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))));
|
||||||
[func!("val": (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))), {})]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Invalid expressions.
|
// Invalid expressions.
|
||||||
p!("[val: 4pt--]" => [func!("val": (Pt(4.0)))], [
|
p!("[val: 4pt--]" => [func!("val": (Len(Length::pt(4.0))))], [
|
||||||
(0:10, 0:11, "dangling minus"),
|
(0:10, 0:11, "dangling minus"),
|
||||||
(0:6, 0:10, "missing right summand")
|
(0:6, 0:10, "missing right summand")
|
||||||
]);
|
]);
|
||||||
p!("[val: 3mm+4pt*]" =>
|
p!("[val: 3mm+4pt*]" =>
|
||||||
[func!("val": (Add(Sz(Size::mm(3.0)), Pt(4.0))))],
|
[func!("val": (Add(Len(Length::mm(3.0)), Len(Length::pt(4.0)))))],
|
||||||
[(0:10, 0:14, "missing right factor")],
|
[(0:10, 0:14, "missing right factor")],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -976,7 +976,7 @@ mod tests {
|
|||||||
// Nested tuples.
|
// Nested tuples.
|
||||||
pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!(
|
pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!(
|
||||||
"css",
|
"css",
|
||||||
Pt(1.0),
|
Len(Length::pt(1.0)),
|
||||||
named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)),
|
named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)),
|
||||||
Str("solid"),
|
Str("solid"),
|
||||||
)));
|
)));
|
||||||
@ -1012,7 +1012,7 @@ mod tests {
|
|||||||
|
|
||||||
// Missing key.
|
// Missing key.
|
||||||
p!("[val: {,}]" => [val()], [(0:7, 0:8, "expected key, found comma")]);
|
p!("[val: {,}]" => [val()], [(0:7, 0:8, "expected key, found comma")]);
|
||||||
p!("[val: { 12pt }]" => [val()], [(0:8, 0:12, "expected key, found size")]);
|
p!("[val: { 12pt }]" => [val()], [(0:8, 0:12, "expected key, found length")]);
|
||||||
p!("[val: { : }]" => [val()], [(0:8, 0:9, "expected key, found colon")]);
|
p!("[val: { : }]" => [val()], [(0:8, 0:9, "expected key, found colon")]);
|
||||||
|
|
||||||
// Missing colon.
|
// Missing colon.
|
||||||
@ -1053,7 +1053,7 @@ mod tests {
|
|||||||
Num(1.0),
|
Num(1.0),
|
||||||
object!(
|
object!(
|
||||||
"ab" => tuple!(),
|
"ab" => tuple!(),
|
||||||
"d" => tuple!(Num(3.0), Pt(14.0)),
|
"d" => tuple!(Num(3.0), Len(Length::pt(14.0))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Bool(false),
|
Bool(false),
|
||||||
@ -1085,7 +1085,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_multiple_mixed_arguments() {
|
fn parse_multiple_mixed_arguments() {
|
||||||
p!("[val: 12pt, key=value]" =>
|
p!("[val: 12pt, key=value]" =>
|
||||||
[func!("val": (Pt(12.0)), { "key" => Id("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, ValidFuncName)],
|
||||||
);
|
);
|
||||||
pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), });
|
pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), });
|
||||||
@ -1144,7 +1144,7 @@ mod tests {
|
|||||||
fn parse_invalid_commas() {
|
fn parse_invalid_commas() {
|
||||||
// Missing commas.
|
// Missing commas.
|
||||||
p!("[val: 1pt 1]" =>
|
p!("[val: 1pt 1]" =>
|
||||||
[func!("val": (Pt(1.0), Num(1.0)), {})],
|
[func!("val": (Len(Length::pt(1.0)), Num(1.0)), {})],
|
||||||
[(0:9, 0:9, "expected comma")],
|
[(0:9, 0:9, "expected comma")],
|
||||||
);
|
);
|
||||||
p!(r#"[val: _"s"]"# =>
|
p!(r#"[val: _"s"]"# =>
|
||||||
|
@ -3,16 +3,14 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use crate::Pass;
|
|
||||||
use crate::func::ParseFunc;
|
use crate::func::ParseFunc;
|
||||||
use super::func::FuncCall;
|
use super::parsing::CallParser;
|
||||||
use super::parsing::ParseState;
|
|
||||||
use super::Model;
|
use super::Model;
|
||||||
|
|
||||||
/// A map from identifiers to function parsers.
|
/// A map from identifiers to function parsers.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
parsers: HashMap<String, Box<Parser>>,
|
parsers: HashMap<String, Box<CallParser>>,
|
||||||
fallback: Box<Parser>,
|
fallback: Box<CallParser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@ -22,7 +20,7 @@ impl Scope {
|
|||||||
where F: ParseFunc<Meta=()> + Model + 'static {
|
where F: ParseFunc<Meta=()> + Model + 'static {
|
||||||
Scope {
|
Scope {
|
||||||
parsers: HashMap::new(),
|
parsers: HashMap::new(),
|
||||||
fallback: parser::<F>(()),
|
fallback: make_parser::<F>(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,17 +41,17 @@ impl Scope {
|
|||||||
where F: ParseFunc + Model + 'static {
|
where F: ParseFunc + Model + 'static {
|
||||||
self.parsers.insert(
|
self.parsers.insert(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
parser::<F>(metadata),
|
make_parser::<F>(metadata),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the parser with the given name if there is one.
|
/// Return the parser with the given name if there is one.
|
||||||
pub fn get_parser(&self, name: &str) -> Option<&Parser> {
|
pub fn get_parser(&self, name: &str) -> Option<&CallParser> {
|
||||||
self.parsers.get(name).map(AsRef::as_ref)
|
self.parsers.get(name).map(AsRef::as_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the fallback parser.
|
/// Return the fallback parser.
|
||||||
pub fn get_fallback_parser(&self) -> &Parser {
|
pub fn get_fallback_parser(&self) -> &CallParser {
|
||||||
&*self.fallback
|
&*self.fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,11 +64,7 @@ impl Debug for Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function which parses the source of a function into a model type which
|
fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<CallParser>
|
||||||
/// implements [`Model`].
|
|
||||||
type Parser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
|
|
||||||
|
|
||||||
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
|
|
||||||
where F: ParseFunc + Model + 'static {
|
where F: ParseFunc + Model + 'static {
|
||||||
Box::new(move |f, s| {
|
Box::new(move |f, s| {
|
||||||
F::parse(f, s, metadata.clone())
|
F::parse(f, s, metadata.clone())
|
||||||
|
@ -4,16 +4,22 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Span offsetting.
|
||||||
|
pub trait Offset {
|
||||||
|
/// Offset all spans contained in `Self` by the given position.
|
||||||
|
fn offset(self, by: Pos) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
/// A vector of spanned values of type `T`.
|
/// A vector of spanned values of type `T`.
|
||||||
pub type SpanVec<T> = Vec<Spanned<T>>;
|
pub type SpanVec<T> = Vec<Spanned<T>>;
|
||||||
|
|
||||||
/// [Offset](Span::offset) all spans in a vector of spanned things by a start
|
impl<T> Offset for SpanVec<T> {
|
||||||
/// position.
|
fn offset(mut self, by: Pos) -> Self {
|
||||||
pub fn offset_spans<T>(
|
for spanned in &mut self {
|
||||||
vec: SpanVec<T>,
|
spanned.span = spanned.span.offset(by);
|
||||||
start: Position,
|
}
|
||||||
) -> impl Iterator<Item=Spanned<T>> {
|
self
|
||||||
vec.into_iter().map(move |s| s.map_span(|span| span.offset(start)))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A value with the span it corresponds to in the source code.
|
/// A value with the span it corresponds to in the source code.
|
||||||
@ -53,6 +59,12 @@ impl<T> Spanned<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Offset for Spanned<T> {
|
||||||
|
fn offset(self, by: Pos) -> Self {
|
||||||
|
self.map_span(|span| span.offset(by))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Debug> Debug for Spanned<T> {
|
impl<T: Debug> Debug for Spanned<T> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
self.v.fmt(f)?;
|
self.v.fmt(f)?;
|
||||||
@ -68,20 +80,25 @@ impl<T: Debug> Debug for Spanned<T> {
|
|||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
/// The inclusive start position.
|
/// The inclusive start position.
|
||||||
pub start: Position,
|
pub start: Pos,
|
||||||
/// The inclusive end position.
|
/// The inclusive end position.
|
||||||
pub end: Position,
|
pub end: Pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
/// The zero span.
|
/// The zero span.
|
||||||
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
|
pub const ZERO: Span = Span { start: Pos::ZERO, end: Pos::ZERO };
|
||||||
|
|
||||||
/// Create a new span from start and end positions.
|
/// Create a new span from start and end positions.
|
||||||
pub fn new(start: Position, end: Position) -> Span {
|
pub fn new(start: Pos, end: Pos) -> Span {
|
||||||
Span { start, end }
|
Span { start, end }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a span including just a single position.
|
||||||
|
pub fn at(pos: Pos) -> Span {
|
||||||
|
Span { start: pos, end: pos }
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new span with the earlier start and later end position.
|
/// Create a new span with the earlier start and later end position.
|
||||||
pub fn merge(a: Span, b: Span) -> Span {
|
pub fn merge(a: Span, b: Span) -> Span {
|
||||||
Span {
|
Span {
|
||||||
@ -90,24 +107,17 @@ impl Span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a span including just a single position.
|
|
||||||
pub fn at(pos: Position) -> Span {
|
|
||||||
Span { start: pos, end: pos }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expand a span by merging it with another span.
|
/// Expand a span by merging it with another span.
|
||||||
pub fn expand(&mut self, other: Span) {
|
pub fn expand(&mut self, other: Span) {
|
||||||
*self = Span::merge(*self, other)
|
*self = Span::merge(*self, other)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Offset a span by a start position.
|
impl Offset for Span {
|
||||||
///
|
fn offset(self, by: Pos) -> Self {
|
||||||
/// This is, for example, used to translate error spans from function local
|
|
||||||
/// to global.
|
|
||||||
pub fn offset(self, start: Position) -> Span {
|
|
||||||
Span {
|
Span {
|
||||||
start: start + self.start,
|
start: self.start.offset(by),
|
||||||
end: start + self.end,
|
end: self.end.offset(by),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,34 +130,40 @@ impl Debug for Span {
|
|||||||
|
|
||||||
/// Zero-indexed line-column position in source code.
|
/// Zero-indexed line-column position in source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
pub struct Position {
|
pub struct Pos {
|
||||||
/// The zero-indexed line.
|
/// The zero-indexed line.
|
||||||
pub line: usize,
|
pub line: usize,
|
||||||
/// The zero-indexed column.
|
/// The zero-indexed column.
|
||||||
pub column: usize,
|
pub column: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Pos {
|
||||||
/// The line 0, column 0 position.
|
/// The line 0, column 0 position.
|
||||||
pub const ZERO: Position = Position { line: 0, column: 0 };
|
pub const ZERO: Pos = Pos { line: 0, column: 0 };
|
||||||
|
|
||||||
/// Create a new position from line and column.
|
/// Create a new position from line and column.
|
||||||
pub fn new(line: usize, column: usize) -> Position {
|
pub fn new(line: usize, column: usize) -> Pos {
|
||||||
Position { line, column }
|
Pos { line, column }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Position {
|
impl Offset for Pos {
|
||||||
type Output = Position;
|
fn offset(self, by: Pos) -> Self {
|
||||||
|
by + self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add(self, rhs: Position) -> Position {
|
impl Add for Pos {
|
||||||
|
type Output = Pos;
|
||||||
|
|
||||||
|
fn add(self, rhs: Pos) -> Pos {
|
||||||
if rhs.line == 0 {
|
if rhs.line == 0 {
|
||||||
Position {
|
Pos {
|
||||||
line: self.line,
|
line: self.line,
|
||||||
column: self.column + rhs.column
|
column: self.column + rhs.column
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Position {
|
Pos {
|
||||||
line: self.line + rhs.line,
|
line: self.line + rhs.line,
|
||||||
column: rhs.column,
|
column: rhs.column,
|
||||||
}
|
}
|
||||||
@ -155,17 +171,17 @@ impl Add for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for Position {
|
impl Sub for Pos {
|
||||||
type Output = Position;
|
type Output = Pos;
|
||||||
|
|
||||||
fn sub(self, rhs: Position) -> Position {
|
fn sub(self, rhs: Pos) -> Pos {
|
||||||
if self.line == rhs.line {
|
if self.line == rhs.line {
|
||||||
Position {
|
Pos {
|
||||||
line: 0,
|
line: 0,
|
||||||
column: self.column - rhs.column
|
column: self.column - rhs.column
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Position {
|
Pos {
|
||||||
line: self.line - rhs.line,
|
line: self.line - rhs.line,
|
||||||
column: self.column,
|
column: self.column,
|
||||||
}
|
}
|
||||||
@ -173,7 +189,7 @@ impl Sub for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Position {
|
impl Debug for Pos {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{}:{}", self.line, self.column)
|
write!(f, "{}:{}", self.line, self.column)
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ macro_rules! span_vec {
|
|||||||
|
|
||||||
macro_rules! span_item {
|
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::{Position, Span, Spanned};
|
use $crate::syntax::span::{Pos, Span, Spanned};
|
||||||
Spanned {
|
Spanned {
|
||||||
span: Span::new(
|
span: Span::new(
|
||||||
Position::new($sl, $sc),
|
Pos::new($sl, $sc),
|
||||||
Position::new($el, $ec)
|
Pos::new($el, $ec)
|
||||||
),
|
),
|
||||||
v: $v
|
v: $v
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use std::iter::Peekable;
|
|||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
use crate::size::Size;
|
use crate::length::Length;
|
||||||
use super::span::{Position, Span, Spanned};
|
use super::span::{Pos, Span, Spanned};
|
||||||
|
|
||||||
use Token::*;
|
use Token::*;
|
||||||
use TokenMode::*;
|
use TokenMode::*;
|
||||||
@ -73,8 +73,8 @@ pub enum Token<'s> {
|
|||||||
},
|
},
|
||||||
/// A number in a function header: `3.14`.
|
/// A number in a function header: `3.14`.
|
||||||
ExprNumber(f64),
|
ExprNumber(f64),
|
||||||
/// A size in a function header: `12pt`.
|
/// A length in a function header: `12pt`.
|
||||||
ExprSize(Size),
|
ExprLength(Length),
|
||||||
/// A boolean in a function header: `true | false`.
|
/// A boolean in a function header: `true | false`.
|
||||||
ExprBool(bool),
|
ExprBool(bool),
|
||||||
/// A hex value in a function header: `#20d82a`.
|
/// A hex value in a function header: `#20d82a`.
|
||||||
@ -129,7 +129,7 @@ impl<'s> Token<'s> {
|
|||||||
ExprIdent(_) => "identifier",
|
ExprIdent(_) => "identifier",
|
||||||
ExprStr { .. } => "string",
|
ExprStr { .. } => "string",
|
||||||
ExprNumber(_) => "number",
|
ExprNumber(_) => "number",
|
||||||
ExprSize(_) => "size",
|
ExprLength(_) => "length",
|
||||||
ExprBool(_) => "bool",
|
ExprBool(_) => "bool",
|
||||||
ExprHex(_) => "hex value",
|
ExprHex(_) => "hex value",
|
||||||
Plus => "plus",
|
Plus => "plus",
|
||||||
@ -153,7 +153,7 @@ pub struct Tokens<'s> {
|
|||||||
src: &'s str,
|
src: &'s str,
|
||||||
mode: TokenMode,
|
mode: TokenMode,
|
||||||
iter: Peekable<Chars<'s>>,
|
iter: Peekable<Chars<'s>>,
|
||||||
position: Position,
|
pos: Pos,
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,12 +172,12 @@ impl<'s> Tokens<'s> {
|
|||||||
///
|
///
|
||||||
/// The first token's span starts an the given `offset` position instead of
|
/// The first token's span starts an the given `offset` position instead of
|
||||||
/// the zero position.
|
/// the zero position.
|
||||||
pub fn new(src: &'s str, offset: Position, mode: TokenMode) -> Tokens<'s> {
|
pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Tokens<'s> {
|
||||||
Tokens {
|
Tokens {
|
||||||
src,
|
src,
|
||||||
mode,
|
mode,
|
||||||
iter: src.chars().peekable(),
|
iter: src.chars().peekable(),
|
||||||
position: offset,
|
pos: offset,
|
||||||
index: 0,
|
index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,8 +190,8 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
/// The line-colunn position in the source at which the last token ends and
|
/// The line-colunn position in the source at which the last token ends and
|
||||||
/// next token will start.
|
/// next token will start.
|
||||||
pub fn pos(&self) -> Position {
|
pub fn pos(&self) -> Pos {
|
||||||
self.position
|
self.pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,14 +315,14 @@ impl<'s> Tokens<'s> {
|
|||||||
}, true, 0, -2).0)
|
}, true, 0, -2).0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_whitespace(&mut self, start: Position) -> Token<'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(|n| !n.is_whitespace(), false, 0, 0);
|
||||||
let end = self.pos();
|
let end = self.pos();
|
||||||
|
|
||||||
Space(end.line - start.line)
|
Space(end.line - start.line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function(&mut self, start: Position) -> Token<'s> {
|
fn read_function(&mut self, start: Pos) -> Token<'s> {
|
||||||
let (header, terminated) = self.read_function_part(Header);
|
let (header, terminated) = self.read_function_part(Header);
|
||||||
self.eat();
|
self.eat();
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
self.eat();
|
self.eat();
|
||||||
match n {
|
match n {
|
||||||
'[' => { self.read_function(Position::ZERO); }
|
'[' => { self.read_function(Pos::ZERO); }
|
||||||
'/' if self.peek() == Some('/') => { self.read_line_comment(); }
|
'/' if self.peek() == Some('/') => { self.read_line_comment(); }
|
||||||
'/' if self.peek() == Some('*') => { self.read_block_comment(); }
|
'/' if self.peek() == Some('*') => { self.read_block_comment(); }
|
||||||
'"' if mode == Header => { self.read_string(); }
|
'"' if mode == Header => { self.read_string(); }
|
||||||
@ -427,8 +427,8 @@ impl<'s> Tokens<'s> {
|
|||||||
ExprNumber(num)
|
ExprNumber(num)
|
||||||
} else if let Some(num) = parse_percentage(text) {
|
} else if let Some(num) = parse_percentage(text) {
|
||||||
ExprNumber(num / 100.0)
|
ExprNumber(num / 100.0)
|
||||||
} else if let Ok(size) = text.parse::<Size>() {
|
} else if let Ok(length) = text.parse::<Length>() {
|
||||||
ExprSize(size)
|
ExprLength(length)
|
||||||
} else if is_identifier(text) {
|
} else if is_identifier(text) {
|
||||||
ExprIdent(text)
|
ExprIdent(text)
|
||||||
} else {
|
} else {
|
||||||
@ -476,10 +476,10 @@ impl<'s> Tokens<'s> {
|
|||||||
self.index += c.len_utf8();
|
self.index += c.len_utf8();
|
||||||
|
|
||||||
if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) {
|
if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) {
|
||||||
self.position.line += 1;
|
self.pos.line += 1;
|
||||||
self.position.column = 0;
|
self.pos.column = 0;
|
||||||
} else {
|
} else {
|
||||||
self.position.column += 1;
|
self.pos.column += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(c)
|
Some(c)
|
||||||
@ -543,7 +543,7 @@ mod tests {
|
|||||||
LeftBrace as LB, RightBrace as RB,
|
LeftBrace as LB, RightBrace as RB,
|
||||||
ExprIdent as Id,
|
ExprIdent as Id,
|
||||||
ExprNumber as Num,
|
ExprNumber as Num,
|
||||||
ExprSize as Sz,
|
ExprLength as Len,
|
||||||
ExprBool as Bool,
|
ExprBool as Bool,
|
||||||
ExprHex as Hex,
|
ExprHex as Hex,
|
||||||
Text as T,
|
Text as T,
|
||||||
@ -557,7 +557,7 @@ mod tests {
|
|||||||
macro_rules! t {
|
macro_rules! t {
|
||||||
($mode:expr, $source:expr => [$($tokens:tt)*]) => {
|
($mode:expr, $source:expr => [$($tokens:tt)*]) => {
|
||||||
let (exp, spans) = span_vec![$($tokens)*];
|
let (exp, spans) = span_vec![$($tokens)*];
|
||||||
let found = Tokens::new($source, Position::ZERO, $mode).collect::<Vec<_>>();
|
let found = Tokens::new($source, Pos::ZERO, $mode).collect::<Vec<_>>();
|
||||||
check($source, exp, found, spans);
|
check($source, exp, found, spans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -640,13 +640,13 @@ mod tests {
|
|||||||
t!(Header, "__main__" => [Id("__main__")]);
|
t!(Header, "__main__" => [Id("__main__")]);
|
||||||
t!(Header, ".func.box" => [Id(".func.box")]);
|
t!(Header, ".func.box" => [Id(".func.box")]);
|
||||||
t!(Header, "arg, _b, _1" => [Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1")]);
|
t!(Header, "arg, _b, _1" => [Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1")]);
|
||||||
t!(Header, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Sz(Size::pt(12.0))]);
|
t!(Header, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0))]);
|
||||||
t!(Header, "1e5in" => [Sz(Size::inches(100000.0))]);
|
t!(Header, "1e5in" => [Len(Length::inches(100000.0))]);
|
||||||
t!(Header, "2.3cm" => [Sz(Size::cm(2.3))]);
|
t!(Header, "2.3cm" => [Len(Length::cm(2.3))]);
|
||||||
t!(Header, "12e-3in" => [Sz(Size::inches(12e-3))]);
|
t!(Header, "12e-3in" => [Len(Length::inches(12e-3))]);
|
||||||
t!(Header, "6.1cm + 4pt,a=1*2" => [Sz(Size::cm(6.1)), S(0), Plus, S(0), Sz(Size::pt(4.0)), Comma, Id("a"), Equals, Num(1.0), Star, Num(2.0)]);
|
t!(Header, "6.1cm + 4pt,a=1*2" => [Len(Length::cm(6.1)), S(0), Plus, S(0), Len(Length::pt(4.0)), Comma, Id("a"), Equals, Num(1.0), Star, Num(2.0)]);
|
||||||
t!(Header, "(5 - 1) / 2.1" => [LP, Num(5.0), S(0), Min, S(0), Num(1.0), RP, S(0), Slash, S(0), Num(2.1)]);
|
t!(Header, "(5 - 1) / 2.1" => [LP, Num(5.0), S(0), Min, S(0), Num(1.0), RP, S(0), Slash, S(0), Num(2.1)]);
|
||||||
t!(Header, "02.4mm" => [Sz(Size::mm(2.4))]);
|
t!(Header, "02.4mm" => [Len(Length::mm(2.4))]);
|
||||||
t!(Header, "2.4.cm" => [Invalid("2.4.cm")]);
|
t!(Header, "2.4.cm" => [Invalid("2.4.cm")]);
|
||||||
t!(Header, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]);
|
t!(Header, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]);
|
||||||
t!(Header, "{abc}" => [LB, Id("abc"), RB]);
|
t!(Header, "{abc}" => [LB, Id("abc"), RB]);
|
||||||
|
@ -12,13 +12,13 @@ use futures_executor::block_on;
|
|||||||
|
|
||||||
use typstc::{Typesetter, DebugErrorProvider};
|
use typstc::{Typesetter, DebugErrorProvider};
|
||||||
use typstc::layout::MultiLayout;
|
use typstc::layout::MultiLayout;
|
||||||
use typstc::size::{Size, Size2D, ValueBox};
|
use typstc::length::{Length, Size, Value4};
|
||||||
use typstc::style::{PageStyle, PaperClass};
|
use typstc::style::PageStyle;
|
||||||
|
use typstc::paper::PaperClass;
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
use toddle::query::FontIndex;
|
use toddle::query::FontIndex;
|
||||||
use toddle::query::fs::EagerFsProvider;
|
use toddle::query::fs::EagerFsProvider;
|
||||||
|
|
||||||
|
|
||||||
type DynResult<T> = Result<T, Box<dyn Error>>;
|
type DynResult<T> = Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
fn main() -> DynResult<()> {
|
fn main() -> DynResult<()> {
|
||||||
@ -72,8 +72,8 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
|||||||
|
|
||||||
typesetter.set_page_style(PageStyle {
|
typesetter.set_page_style(PageStyle {
|
||||||
class: PaperClass::Custom,
|
class: PaperClass::Custom,
|
||||||
dimensions: Size2D::with_all(Size::pt(250.0)),
|
dimensions: Size::with_all(Length::pt(250.0)),
|
||||||
margins: ValueBox::with_all(None),
|
margins: Value4::with_all(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
let layouts = compile(&typesetter, src);
|
let layouts = compile(&typesetter, src);
|
||||||
@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
|||||||
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
let typeset = block_on(typesetter.typeset(src));
|
let typeset = block_on(typesetter.typeset(src));
|
||||||
let problems = typeset.feedback.problems;
|
let diagnostics = typeset.feedback.diagnostics;
|
||||||
|
|
||||||
if !problems.is_empty() {
|
if !diagnostics.is_empty() {
|
||||||
for problem in problems {
|
for diagnostic in diagnostics {
|
||||||
println!(" {:?} {:?}: {}",
|
println!(" {:?} {:?}: {}",
|
||||||
problem.v.severity,
|
diagnostic.v.level,
|
||||||
problem.span,
|
diagnostic.span,
|
||||||
problem.v.message
|
diagnostic.v.message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user