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::export::pdf;
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
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::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
|
||||
/// 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(
|
||||
0.0,
|
||||
0.0,
|
||||
page.dimensions.x.to_pt(),
|
||||
page.dimensions.y.to_pt(),
|
||||
page.dimensions.x.to_pt() as f32,
|
||||
page.dimensions.y.to_pt() as f32,
|
||||
);
|
||||
|
||||
self.writer.write_obj(
|
||||
@ -167,14 +166,14 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
|
||||
LayoutAction::SetFont(id, size) => {
|
||||
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) => {
|
||||
if let Some(pos) = next_pos.take() {
|
||||
let x = pos.x.to_pt();
|
||||
let y = (page.dimensions.y - pos.y - Size::pt(active_font.1)).to_pt();
|
||||
text.tm(1.0, 0.0, 0.0, 1.0, x, y);
|
||||
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 as f32, y as f32);
|
||||
}
|
||||
|
||||
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.
|
||||
let head = font.read_table::<Header>()?;
|
||||
|
||||
let font_unit_ratio = 1.0 / (head.units_per_em as f32);
|
||||
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
|
||||
let font_unit_ratio = 1.0 / (head.units_per_em as f64);
|
||||
let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
|
||||
let font_unit_to_glyph_unit = |fu| {
|
||||
let size = font_unit_to_size(fu);
|
||||
(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 bounding_box = Rect::new(
|
||||
font_unit_to_glyph_unit(head.x_min as f32),
|
||||
font_unit_to_glyph_unit(head.y_min as f32),
|
||||
font_unit_to_glyph_unit(head.x_max as f32),
|
||||
font_unit_to_glyph_unit(head.y_max as f32),
|
||||
font_unit_to_glyph_unit(head.x_min as f64),
|
||||
font_unit_to_glyph_unit(head.y_min as f64),
|
||||
font_unit_to_glyph_unit(head.x_max as f64),
|
||||
font_unit_to_glyph_unit(head.y_max as f64),
|
||||
);
|
||||
|
||||
// Transform the width into PDF units.
|
||||
@ -239,7 +238,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
.read_table::<HorizontalMetrics>()?
|
||||
.metrics
|
||||
.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();
|
||||
|
||||
// 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).
|
||||
self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle)
|
||||
.font_bbox(bounding_box)
|
||||
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32))
|
||||
.descent(font_unit_to_glyph_unit(os2.s_typo_descender 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 f64))
|
||||
.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)
|
||||
)?;
|
||||
|
||||
|
@ -52,8 +52,8 @@ pub trait ParseFunc {
|
||||
///
|
||||
/// parse(header, body, state, f) {
|
||||
/// let body = body!(opt: body, state, f);
|
||||
/// let hidden = header.args.pos.get::<bool>(&mut f.problems)
|
||||
/// .or_missing(&mut f.problems, header.name.span, "hidden")
|
||||
/// let hidden = header.args.pos.get::<bool>(&mut f.diagnostics)
|
||||
/// .or_missing(&mut f.diagnostics, header.name.span, "hidden")
|
||||
/// .unwrap_or(false);
|
||||
///
|
||||
/// HiderFunc { body: if hidden { None } else { body } }
|
||||
|
@ -4,23 +4,22 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use serde::ser::{Serialize, Serializer, SerializeTuple};
|
||||
use toddle::query::FontIndex;
|
||||
|
||||
use crate::size::{Size, Size2D};
|
||||
use crate::length::{Length, Size};
|
||||
use super::Layout;
|
||||
use self::LayoutAction::*;
|
||||
|
||||
|
||||
/// A layouting action, which is the basic building block layouts are composed
|
||||
/// of.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LayoutAction {
|
||||
/// Move to an absolute position.
|
||||
MoveAbsolute(Size2D),
|
||||
MoveAbsolute(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.
|
||||
WriteText(String),
|
||||
/// Visualize a box for debugging purposes.
|
||||
DebugBox(Size2D),
|
||||
DebugBox(Size),
|
||||
}
|
||||
|
||||
impl Serialize for LayoutAction {
|
||||
@ -81,11 +80,11 @@ impl Debug for LayoutAction {
|
||||
/// position.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LayoutActions {
|
||||
origin: Size2D,
|
||||
origin: Size,
|
||||
actions: Vec<LayoutAction>,
|
||||
active_font: (FontIndex, Size),
|
||||
next_pos: Option<Size2D>,
|
||||
next_font: Option<(FontIndex, Size)>,
|
||||
active_font: (FontIndex, Length),
|
||||
next_pos: Option<Size>,
|
||||
next_font: Option<(FontIndex, Length)>,
|
||||
}
|
||||
|
||||
impl LayoutActions {
|
||||
@ -93,8 +92,8 @@ impl LayoutActions {
|
||||
pub fn new() -> LayoutActions {
|
||||
LayoutActions {
|
||||
actions: vec![],
|
||||
origin: Size2D::ZERO,
|
||||
active_font: (FontIndex::MAX, Size::ZERO),
|
||||
origin: Size::ZERO,
|
||||
active_font: (FontIndex::MAX, Length::ZERO),
|
||||
next_pos: None,
|
||||
next_font: None,
|
||||
}
|
||||
@ -126,7 +125,7 @@ impl LayoutActions {
|
||||
|
||||
/// Add a layout at a position. All move actions inside the layout are
|
||||
/// 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.origin = position;
|
||||
|
@ -11,7 +11,6 @@
|
||||
use super::stack::{StackLayouter, StackContext};
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Performs the line layouting.
|
||||
#[derive(Debug)]
|
||||
pub struct LineLayouter {
|
||||
@ -40,7 +39,7 @@ pub struct LineContext {
|
||||
/// extent of the layout.
|
||||
pub debug: bool,
|
||||
/// 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
|
||||
@ -49,10 +48,10 @@ pub struct LineContext {
|
||||
#[derive(Debug)]
|
||||
struct LineRun {
|
||||
/// The so-far accumulated layouts in the line.
|
||||
layouts: Vec<(Size, Layout)>,
|
||||
/// The width (primary size) and maximal height (secondary size) of the
|
||||
layouts: Vec<(Length, Layout)>,
|
||||
/// The width (primary length) and maximal height (secondary length) of the
|
||||
/// line.
|
||||
size: Size2D,
|
||||
size: Size,
|
||||
/// The alignment of all layouts in the line.
|
||||
///
|
||||
/// When a new run is created the alignment is yet to be determined. Once a
|
||||
@ -61,7 +60,7 @@ struct LineRun {
|
||||
alignment: Option<LayoutAlignment>,
|
||||
/// 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.
|
||||
usable: Option<Size>,
|
||||
usable: Option<Length>,
|
||||
/// A possibly cached soft spacing or spacing state.
|
||||
last_spacing: LastSpacing,
|
||||
}
|
||||
@ -156,7 +155,7 @@ impl LineLayouter {
|
||||
///
|
||||
/// This specifies how much more fits before a line break needs to be
|
||||
/// issued.
|
||||
fn usable(&self) -> Size2D {
|
||||
fn usable(&self) -> Size {
|
||||
// The base is the usable space per stack layouter.
|
||||
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.
|
||||
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 {
|
||||
// A hard space is simply an empty box.
|
||||
SpacingKind::Hard => {
|
||||
@ -197,7 +196,7 @@ impl LineLayouter {
|
||||
}
|
||||
|
||||
/// 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.stack.add_spacing(spacing, kind)
|
||||
}
|
||||
@ -219,7 +218,7 @@ impl LineLayouter {
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
@ -235,7 +234,7 @@ impl LineLayouter {
|
||||
|
||||
/// Whether the currently set line is empty.
|
||||
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.
|
||||
@ -265,7 +264,7 @@ impl LineLayouter {
|
||||
- layout.dimensions.primary(self.ctx.axes),
|
||||
};
|
||||
|
||||
let pos = Size2D::with_x(x);
|
||||
let pos = Size::with_x(x);
|
||||
actions.add_layout(pos, layout);
|
||||
}
|
||||
|
||||
@ -293,7 +292,7 @@ impl LineRun {
|
||||
fn new() -> LineRun {
|
||||
LineRun {
|
||||
layouts: vec![],
|
||||
size: Size2D::ZERO,
|
||||
size: Size::ZERO,
|
||||
alignment: None,
|
||||
usable: None,
|
||||
last_spacing: LastSpacing::Hard,
|
||||
|
@ -5,7 +5,7 @@ use smallvec::SmallVec;
|
||||
use serde::Serialize;
|
||||
use toddle::query::FontIndex;
|
||||
|
||||
use crate::size::{Size, Size2D, SizeBox};
|
||||
use crate::length::{Length, Size, Margins};
|
||||
use self::prelude::*;
|
||||
|
||||
pub mod line;
|
||||
@ -27,7 +27,6 @@ pub mod prelude {
|
||||
pub use super::Alignment::{self, *};
|
||||
}
|
||||
|
||||
|
||||
/// A collection of layouts.
|
||||
pub type MultiLayout = Vec<Layout>;
|
||||
|
||||
@ -35,7 +34,7 @@ pub type MultiLayout = Vec<Layout>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub struct Layout {
|
||||
/// The size of the box.
|
||||
pub dimensions: Size2D,
|
||||
pub dimensions: Size,
|
||||
/// How to align this layout in a parent container.
|
||||
#[serde(skip)]
|
||||
pub alignment: LayoutAlignment,
|
||||
@ -66,9 +65,9 @@ pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct LayoutSpace {
|
||||
/// The maximum size of the box to layout in.
|
||||
pub dimensions: Size2D,
|
||||
pub dimensions: Size,
|
||||
/// 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
|
||||
/// dimensions of this space or to shrink them to fit the content.
|
||||
pub expansion: LayoutExpansion,
|
||||
@ -77,12 +76,12 @@ pub struct LayoutSpace {
|
||||
impl LayoutSpace {
|
||||
/// The offset from the origin to the start of content, that is,
|
||||
/// `(padding.left, padding.top)`.
|
||||
pub fn start(&self) -> Size2D {
|
||||
Size2D::new(self.padding.left, self.padding.top)
|
||||
pub fn start(&self) -> Size {
|
||||
Size::new(self.padding.left, self.padding.top)
|
||||
}
|
||||
|
||||
/// The actually usable area (dimensions minus padding).
|
||||
pub fn usable(&self) -> Size2D {
|
||||
pub fn usable(&self) -> Size {
|
||||
self.dimensions.unpadded(self.padding)
|
||||
}
|
||||
|
||||
@ -90,7 +89,7 @@ impl LayoutSpace {
|
||||
pub fn usable_space(&self) -> LayoutSpace {
|
||||
LayoutSpace {
|
||||
dimensions: self.usable(),
|
||||
padding: SizeBox::ZERO,
|
||||
padding: Margins::ZERO,
|
||||
expansion: LayoutExpansion::new(false, false),
|
||||
}
|
||||
}
|
||||
@ -369,17 +368,17 @@ enum LastSpacing {
|
||||
/// The last item was hard spacing.
|
||||
Hard,
|
||||
/// The last item was soft spacing with the given width and level.
|
||||
Soft(Size, u32),
|
||||
Soft(Length, u32),
|
||||
/// The last item was not spacing.
|
||||
None,
|
||||
}
|
||||
|
||||
impl LastSpacing {
|
||||
/// The size of the soft space if this is a soft space or zero otherwise.
|
||||
fn soft_or_zero(self) -> Size {
|
||||
/// The length of the soft space if this is a soft space or zero otherwise.
|
||||
fn soft_or_zero(self) -> Length {
|
||||
match self {
|
||||
LastSpacing::Soft(space, _) => space,
|
||||
_ => Size::ZERO,
|
||||
_ => Length::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,14 +10,13 @@ use toddle::query::FontStyle;
|
||||
use crate::{Pass, Feedback};
|
||||
use crate::GlobalFontLoader;
|
||||
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::span::{Span, Spanned};
|
||||
use super::line::{LineLayouter, LineContext};
|
||||
use super::text::{layout_text, TextContext};
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Performs the model layouting.
|
||||
#[derive(Debug)]
|
||||
pub struct ModelLayouter<'a> {
|
||||
@ -36,7 +35,7 @@ pub struct LayoutContext<'a> {
|
||||
/// The style for pages and text.
|
||||
pub style: &'a LayoutStyle,
|
||||
/// The base unpadded dimensions of this container (for relative sizing).
|
||||
pub base: Size2D,
|
||||
pub base: Size,
|
||||
/// The spaces to layout in.
|
||||
pub spaces: LayoutSpaces,
|
||||
/// 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
|
||||
/// secondary axis. The spacing kind defines how the spacing interacts with
|
||||
/// surrounding spacing.
|
||||
AddSpacing(Size, SpacingKind, GenericAxis),
|
||||
AddSpacing(Length, SpacingKind, GenericAxis),
|
||||
|
||||
/// Start a new line.
|
||||
BreakLine,
|
||||
@ -159,7 +158,7 @@ impl<'a> ModelLayouter<'a> {
|
||||
|
||||
for Spanned { v: node, span } in &model.nodes {
|
||||
let decorate = |this: &mut ModelLayouter, deco| {
|
||||
this.feedback.decos.push(Spanned::new(deco, *span));
|
||||
this.feedback.decorations.push(Spanned::new(deco, *span));
|
||||
};
|
||||
|
||||
match node {
|
||||
|
@ -22,10 +22,9 @@
|
||||
//! sentence in the second box.
|
||||
|
||||
use smallvec::smallvec;
|
||||
use crate::size::ValueBox;
|
||||
use crate::length::Value4;
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Performs the stack layouting.
|
||||
#[derive(Debug)]
|
||||
pub struct StackLayouter {
|
||||
@ -66,14 +65,14 @@ struct Space {
|
||||
/// The so-far accumulated layouts.
|
||||
layouts: Vec<(LayoutAxes, Layout)>,
|
||||
/// The specialized size of this space.
|
||||
size: Size2D,
|
||||
size: Size,
|
||||
/// The specialized remaining space.
|
||||
usable: Size2D,
|
||||
usable: Size,
|
||||
/// 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
|
||||
/// 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.
|
||||
last_spacing: LastSpacing,
|
||||
}
|
||||
@ -129,13 +128,13 @@ impl StackLayouter {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
// A hard space is simply an empty box.
|
||||
SpacingKind::Hard => {
|
||||
// Reduce the spacing such that it definitely fits.
|
||||
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.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
|
||||
/// 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 mut size = self.space.size.generalized(axes);
|
||||
let mut extra = self.space.extra.generalized(axes);
|
||||
|
||||
size.x += (dimensions.x - extra.x).max(Size::ZERO);
|
||||
size.y += (dimensions.y - extra.y).max(Size::ZERO);
|
||||
size.x += (dimensions.x - extra.x).max(Length::ZERO);
|
||||
size.y += (dimensions.y - extra.y).max(Length::ZERO);
|
||||
|
||||
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.extra = extra.specialized(axes);
|
||||
@ -233,7 +232,7 @@ impl StackLayouter {
|
||||
|
||||
/// Move to the first space that can fit the given dimensions or do nothing
|
||||
/// 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();
|
||||
for (index, space) in self.ctx.spaces[start..].iter().enumerate() {
|
||||
if space.usable().fits(dimensions) {
|
||||
@ -251,7 +250,7 @@ impl StackLayouter {
|
||||
|
||||
let mut spaces = smallvec![LayoutSpace {
|
||||
dimensions,
|
||||
padding: SizeBox::ZERO,
|
||||
padding: Margins::ZERO,
|
||||
expansion: LayoutExpansion::new(false, false),
|
||||
}];
|
||||
|
||||
@ -263,15 +262,15 @@ impl StackLayouter {
|
||||
}
|
||||
|
||||
/// The remaining usable size.
|
||||
pub fn usable(&self) -> Size2D {
|
||||
pub fn usable(&self) -> Size {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Whether the current layout space (not subspace) is empty.
|
||||
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.
|
||||
@ -310,7 +309,7 @@ impl StackLayouter {
|
||||
let start = space.start();
|
||||
|
||||
let mut bounds = vec![];
|
||||
let mut bound = SizeBox {
|
||||
let mut bound = Margins {
|
||||
left: start.x,
|
||||
top: start.y,
|
||||
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
|
||||
// run, while the `y` fields stores the accumulated secondary extent.
|
||||
let mut extent = Size2D::ZERO;
|
||||
let mut extent = Size::ZERO;
|
||||
let mut rotation = Vertical;
|
||||
|
||||
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.
|
||||
if rotation != axes.secondary.axis() {
|
||||
extent.y = extent.x;
|
||||
extent.x = Size::ZERO;
|
||||
extent.x = Length::ZERO;
|
||||
rotation = axes.secondary.axis();
|
||||
}
|
||||
|
||||
@ -383,11 +382,11 @@ impl StackLayouter {
|
||||
// The space in which this layout is aligned is given by the
|
||||
// distances between the borders of it's bounding box.
|
||||
let usable =
|
||||
Size2D::new(bound.right - bound.left, bound.bottom - bound.top)
|
||||
Size::new(bound.right - bound.left, bound.bottom - bound.top)
|
||||
.generalized(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);
|
||||
}
|
||||
@ -417,15 +416,15 @@ impl StackLayouter {
|
||||
}
|
||||
|
||||
impl Space {
|
||||
fn new(index: usize, hard: bool, usable: Size2D) -> Space {
|
||||
fn new(index: usize, hard: bool, usable: Size) -> Space {
|
||||
Space {
|
||||
index,
|
||||
hard,
|
||||
layouts: vec![],
|
||||
size: Size2D::ZERO,
|
||||
size: Size::ZERO,
|
||||
usable,
|
||||
extra: Size2D::ZERO,
|
||||
rulers: ValueBox::with_all(Origin),
|
||||
extra: Size::ZERO,
|
||||
rulers: Value4::with_all(Origin),
|
||||
last_spacing: LastSpacing::Hard,
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,10 @@ use toddle::query::{FontQuery, FontIndex};
|
||||
use toddle::tables::{CharMap, Header, HorizontalMetrics};
|
||||
|
||||
use crate::GlobalFontLoader;
|
||||
use crate::size::{Size, Size2D};
|
||||
use crate::length::{Length, Size};
|
||||
use crate::style::TextStyle;
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Performs the text layouting.
|
||||
#[derive(Debug)]
|
||||
struct TextLayouter<'a> {
|
||||
@ -21,7 +20,7 @@ struct TextLayouter<'a> {
|
||||
actions: LayoutActions,
|
||||
buffer: String,
|
||||
active_font: FontIndex,
|
||||
width: Size,
|
||||
width: Length,
|
||||
}
|
||||
|
||||
/// The context for text layouting.
|
||||
@ -54,7 +53,7 @@ impl<'a> TextLayouter<'a> {
|
||||
actions: LayoutActions::new(),
|
||||
buffer: String::new(),
|
||||
active_font: FontIndex::MAX,
|
||||
width: Size::ZERO,
|
||||
width: Length::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +76,7 @@ impl<'a> TextLayouter<'a> {
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
/// 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 variant = self.ctx.style.variant;
|
||||
@ -127,8 +126,8 @@ impl<'a> TextLayouter<'a> {
|
||||
if let Some((font, index)) = loader.get(query).await {
|
||||
// Determine the width of the char.
|
||||
let header = font.read_table::<Header>().ok()?;
|
||||
let font_unit_ratio = 1.0 / (header.units_per_em as f32);
|
||||
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
|
||||
let font_unit_ratio = 1.0 / (header.units_per_em as f64);
|
||||
let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
|
||||
|
||||
let glyph = font
|
||||
.read_table::<CharMap>()
|
||||
@ -139,7 +138,7 @@ impl<'a> TextLayouter<'a> {
|
||||
.read_table::<HorizontalMetrics>()
|
||||
.ok()?
|
||||
.get(glyph)?
|
||||
.advance_width as f32;
|
||||
.advance_width as f64;
|
||||
|
||||
let char_width = font_unit_to_size(glyph_width)
|
||||
* self.ctx.style.font_size().to_pt();
|
||||
|
@ -8,173 +8,166 @@ use serde::Serialize;
|
||||
|
||||
use crate::layout::prelude::*;
|
||||
|
||||
|
||||
/// A general spacing type.
|
||||
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Size {
|
||||
/// The size in typographic points (1/72 inches).
|
||||
pub points: f32,
|
||||
pub struct Length {
|
||||
/// The length in typographic points (1/72 inches).
|
||||
pub points: f64,
|
||||
}
|
||||
|
||||
impl Size {
|
||||
/// The zeroed size.
|
||||
pub const ZERO: Size = Size { points: 0.0 };
|
||||
impl Length {
|
||||
/// The zeroed length.
|
||||
pub const ZERO: Length = Length { points: 0.0 };
|
||||
|
||||
/// Create a size from an amount of points.
|
||||
pub fn pt(points: f32) -> Size { Size { points } }
|
||||
/// Create a length from an amount of points.
|
||||
pub fn pt(points: f64) -> Length { Length { points } }
|
||||
|
||||
/// Create a size from an amount of millimeters.
|
||||
pub fn mm(mm: f32) -> Size { Size { points: 2.83465 * mm } }
|
||||
/// Create a length from an amount of millimeters.
|
||||
pub fn mm(mm: f64) -> Length { Length { points: 2.83465 * mm } }
|
||||
|
||||
/// Create a size from an amount of centimeters.
|
||||
pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } }
|
||||
/// Create a length from an amount of centimeters.
|
||||
pub fn cm(cm: f64) -> Length { Length { points: 28.3465 * cm } }
|
||||
|
||||
/// Create a size from an amount of inches.
|
||||
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } }
|
||||
/// Create a length from an amount of inches.
|
||||
pub fn inches(inches: f64) -> Length { Length { points: 72.0 * inches } }
|
||||
|
||||
/// Convert this size into points.
|
||||
pub fn to_pt(self) -> f32 { self.points }
|
||||
/// Convert this length into points.
|
||||
pub fn to_pt(self) -> f64 { self.points }
|
||||
|
||||
/// Convert this size into millimeters.
|
||||
pub fn to_mm(self) -> f32 { self.points * 0.352778 }
|
||||
/// Convert this length into millimeters.
|
||||
pub fn to_mm(self) -> f64 { self.points * 0.352778 }
|
||||
|
||||
/// Convert this size into centimeters.
|
||||
pub fn to_cm(self) -> f32 { self.points * 0.0352778 }
|
||||
/// Convert this length into centimeters.
|
||||
pub fn to_cm(self) -> f64 { self.points * 0.0352778 }
|
||||
|
||||
/// Convert this size into inches.
|
||||
pub fn to_inches(self) -> f32 { self.points * 0.0138889 }
|
||||
/// Convert this length into inches.
|
||||
pub fn to_inches(self) -> f64 { self.points * 0.0138889 }
|
||||
|
||||
/// The maximum of this and the other size.
|
||||
pub fn max(self, other: Size) -> Size {
|
||||
/// The maximum of this and the other length.
|
||||
pub fn max(self, other: Length) -> Length {
|
||||
if self > other { self } else { other }
|
||||
}
|
||||
|
||||
/// The minimum of this and the other size.
|
||||
pub fn min(self, other: Size) -> Size {
|
||||
/// The minimum of this and the other length.
|
||||
pub fn min(self, other: Length) -> Length {
|
||||
if self <= other { self } else { other }
|
||||
}
|
||||
|
||||
/// Set this size to the maximum of itself and the other size.
|
||||
pub fn max_eq(&mut self, other: Size) { *self = self.max(other); }
|
||||
/// Set this length to the maximum of itself and the other length.
|
||||
pub fn max_eq(&mut self, other: Length) { *self = self.max(other); }
|
||||
|
||||
/// Set this size to the minimum of itself and the other size.
|
||||
pub fn min_eq(&mut self, other: Size) { *self = self.min(other); }
|
||||
/// Set this length to the minimum of itself and the other length.
|
||||
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
|
||||
/// alignment in a container with this size.
|
||||
pub fn anchor(self, alignment: Alignment, direction: Direction) -> Size {
|
||||
/// alignment in a container with this length.
|
||||
pub fn anchor(self, alignment: Alignment, direction: Direction) -> Length {
|
||||
match (direction.is_positive(), alignment) {
|
||||
(true, Origin) | (false, End) => Size::ZERO,
|
||||
(true, Origin) | (false, End) => Length::ZERO,
|
||||
(_, Center) => self / 2,
|
||||
(true, End) | (false, Origin) => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Size {
|
||||
impl Display for Length {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}pt", self.points)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Size {
|
||||
impl Debug for Length {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Size {
|
||||
type Output = Size;
|
||||
impl Neg for Length {
|
||||
type Output = Length;
|
||||
|
||||
fn neg(self) -> Size {
|
||||
Size { points: -self.points }
|
||||
fn neg(self) -> Length {
|
||||
Length { points: -self.points }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum for Size {
|
||||
fn sum<I>(iter: I) -> Size
|
||||
where I: Iterator<Item = Size> {
|
||||
iter.fold(Size::ZERO, Add::add)
|
||||
impl Sum for Length {
|
||||
fn sum<I>(iter: I) -> Length
|
||||
where I: Iterator<Item = Length> {
|
||||
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)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ScaleSize {
|
||||
Absolute(Size),
|
||||
Scaled(f32),
|
||||
pub enum ScaleLength {
|
||||
Absolute(Length),
|
||||
Scaled(f64),
|
||||
}
|
||||
|
||||
impl ScaleSize {
|
||||
impl ScaleLength {
|
||||
/// Use the absolute value or scale the entity.
|
||||
pub fn scaled(&self, entity: Size) -> Size {
|
||||
pub fn scaled(&self, entity: Length) -> Length {
|
||||
match self {
|
||||
ScaleSize::Absolute(s) => *s,
|
||||
ScaleSize::Scaled(s) => *s * entity,
|
||||
ScaleLength::Absolute(s) => *s,
|
||||
ScaleLength::Scaled(s) => *s * entity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ScaleSize {
|
||||
impl Display for ScaleLength {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ScaleSize::Absolute(size) => write!(f, "{}", size),
|
||||
ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
||||
ScaleLength::Absolute(length) => write!(f, "{}", length),
|
||||
ScaleLength::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ScaleSize {
|
||||
impl Debug for ScaleLength {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
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.
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||
pub struct Value2D<T> {
|
||||
pub struct Value2<T> {
|
||||
/// The horizontal component.
|
||||
pub x: T,
|
||||
/// The vertical component.
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Value2D<T> {
|
||||
impl<T: Clone> Value2<T> {
|
||||
/// 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.
|
||||
pub fn with_x(x: T) -> Value2D<T> where T: Default {
|
||||
Value2D { x, y: T::default() }
|
||||
pub fn with_x(x: T) -> Value2<T> where T: Default {
|
||||
Value2 { x, y: T::default() }
|
||||
}
|
||||
|
||||
/// Create a new 2D-value with `y` set to a value and `x` to default.
|
||||
pub fn with_y(y: T) -> Value2D<T> where T: Default {
|
||||
Value2D { x: T::default(), y }
|
||||
pub fn with_y(y: T) -> Value2<T> where T: Default {
|
||||
Value2 { x: T::default(), y }
|
||||
}
|
||||
|
||||
/// Create a new 2D-value with the primary axis set to a value and the other
|
||||
/// one to default.
|
||||
pub fn with_primary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default {
|
||||
Value2D::with_x(v).generalized(axes)
|
||||
pub fn with_primary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
|
||||
Value2::with_x(v).generalized(axes)
|
||||
}
|
||||
|
||||
/// Create a new 2D-value with the secondary axis set to a value and the
|
||||
/// other one to default.
|
||||
pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default {
|
||||
Value2D::with_y(v).generalized(axes)
|
||||
pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
|
||||
Value2::with_y(v).generalized(axes)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn get(self, axis: SpecificAxis) -> T {
|
||||
@ -216,16 +209,16 @@ impl<T: Clone> Value2D<T> {
|
||||
/// axes, that is:
|
||||
/// - `x` describes the primary axis instead of the horizontal one.
|
||||
/// - `y` describes the secondary axis instead of the vertical one.
|
||||
pub fn generalized(self, axes: LayoutAxes) -> Value2D<T> {
|
||||
pub fn generalized(self, axes: LayoutAxes) -> Value2<T> {
|
||||
match axes.primary.axis() {
|
||||
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
|
||||
/// `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
|
||||
// at the call site, we still have this second function.
|
||||
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 {
|
||||
f.debug_list()
|
||||
.entry(&self.x)
|
||||
@ -247,83 +240,61 @@ impl<T> Debug for Value2D<T> where T: Debug {
|
||||
}
|
||||
|
||||
/// A position or extent in 2-dimensional space.
|
||||
pub type Size2D = Value2D<Size>;
|
||||
pub type Size = Value2<Length>;
|
||||
|
||||
impl Size2D {
|
||||
/// The zeroed 2D-size.
|
||||
pub const ZERO: Size2D = Size2D { x: Size::ZERO, y: Size::ZERO };
|
||||
impl Size {
|
||||
/// The zeroed 2D-length.
|
||||
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.
|
||||
pub fn fits(self, other: Size2D) -> bool {
|
||||
pub fn fits(self, other: Size) -> bool {
|
||||
self.x >= other.x && self.y >= other.y
|
||||
}
|
||||
|
||||
/// Return a 2D-size padded by the paddings of the given box.
|
||||
pub fn padded(self, padding: SizeBox) -> Size2D {
|
||||
Size2D {
|
||||
/// Return a 2D-length padded by the paddings of the given box.
|
||||
pub fn padded(self, padding: Margins) -> Size {
|
||||
Size {
|
||||
x: self.x + padding.left + padding.right,
|
||||
y: self.y + padding.top + padding.bottom,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a 2D-size reduced by the paddings of the given box.
|
||||
pub fn unpadded(self, padding: SizeBox) -> Size2D {
|
||||
Size2D {
|
||||
/// Return a 2D-length reduced by the paddings of the given box.
|
||||
pub fn unpadded(self, padding: Margins) -> Size {
|
||||
Size {
|
||||
x: self.x - padding.left - padding.right,
|
||||
y: self.y - padding.top - padding.bottom,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size2D {
|
||||
Size2D {
|
||||
pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size {
|
||||
Size {
|
||||
x: self.x.anchor(alignment.primary, axes.primary),
|
||||
y: self.y.anchor(alignment.secondary, axes.secondary),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Size2D {
|
||||
type Output = Size2D;
|
||||
impl Neg for Size {
|
||||
type Output = Size;
|
||||
|
||||
fn neg(self) -> Size2D {
|
||||
Size2D {
|
||||
fn neg(self) -> Size {
|
||||
Size {
|
||||
x: -self.x,
|
||||
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.
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||
pub struct ValueBox<T> {
|
||||
pub struct Value4<T> {
|
||||
/// The left extent.
|
||||
pub left: T,
|
||||
/// The top extent.
|
||||
@ -334,15 +305,15 @@ pub struct ValueBox<T> {
|
||||
pub bottom: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> ValueBox<T> {
|
||||
impl<T: Clone> Value4<T> {
|
||||
/// Create a new box from four sizes.
|
||||
pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> {
|
||||
ValueBox { left, top, right, bottom }
|
||||
pub fn new(left: T, top: T, right: T, bottom: T) -> Value4<T> {
|
||||
Value4 { left, top, right, bottom }
|
||||
}
|
||||
|
||||
/// Create a box with all four fields set to the same value `s`.
|
||||
pub fn with_all(value: T) -> ValueBox<T> {
|
||||
ValueBox {
|
||||
pub fn with_all(value: T) -> Value4<T> {
|
||||
Value4 {
|
||||
left: value.clone(),
|
||||
top: value.clone(),
|
||||
right: value.clone(),
|
||||
@ -369,7 +340,7 @@ impl<T: Clone> ValueBox<T> {
|
||||
|
||||
/// Set all values to the given value.
|
||||
pub fn set_all(&mut self, value: T) {
|
||||
*self = ValueBox::with_all(value);
|
||||
*self = Value4::with_all(value);
|
||||
}
|
||||
|
||||
/// Set the `left` and `right` values.
|
||||
@ -385,47 +356,47 @@ impl<T: Clone> ValueBox<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A size in four dimensions.
|
||||
pub type SizeBox = ValueBox<Size>;
|
||||
/// A length in four dimensions.
|
||||
pub type Margins = Value4<Length>;
|
||||
|
||||
impl SizeBox {
|
||||
/// The zeroed size box.
|
||||
pub const ZERO: SizeBox = SizeBox {
|
||||
left: Size::ZERO,
|
||||
top: Size::ZERO,
|
||||
right: Size::ZERO,
|
||||
bottom: Size::ZERO,
|
||||
impl Margins {
|
||||
/// The zeroed length box.
|
||||
pub const ZERO: Margins = Margins {
|
||||
left: Length::ZERO,
|
||||
top: Length::ZERO,
|
||||
right: Length::ZERO,
|
||||
bottom: Length::ZERO,
|
||||
};
|
||||
}
|
||||
|
||||
impl FromStr for Size {
|
||||
type Err = ParseSizeError;
|
||||
impl FromStr for Length {
|
||||
type Err = ParseLengthError;
|
||||
|
||||
fn from_str(src: &str) -> Result<Size, ParseSizeError> {
|
||||
fn from_str(src: &str) -> Result<Length, ParseLengthError> {
|
||||
let func = match () {
|
||||
_ if src.ends_with("pt") => Size::pt,
|
||||
_ if src.ends_with("mm") => Size::mm,
|
||||
_ if src.ends_with("cm") => Size::cm,
|
||||
_ if src.ends_with("in") => Size::inches,
|
||||
_ => return Err(ParseSizeError(())),
|
||||
_ if src.ends_with("pt") => Length::pt,
|
||||
_ if src.ends_with("mm") => Length::mm,
|
||||
_ if src.ends_with("cm") => Length::cm,
|
||||
_ if src.ends_with("in") => Length::inches,
|
||||
_ => return Err(ParseLengthError),
|
||||
};
|
||||
|
||||
Ok(func(src[..src.len() - 2]
|
||||
.parse::<f32>()
|
||||
.map_err(|_| ParseSizeError(()))?))
|
||||
.parse::<f64>()
|
||||
.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)]
|
||||
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 {
|
||||
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 f32, $ty $t $o $($rest)*);)*
|
||||
$(implement_traits!(@$w f64, $ty $t $o $($rest)*);)*
|
||||
};
|
||||
|
||||
(@front $num:ty, $ty:ident $t:ident $o:ident
|
||||
@ -458,7 +429,7 @@ macro_rules! implement_traits {
|
||||
impl $tr<$ty> for $num {
|
||||
type Output = $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 {
|
||||
type Output = $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 {
|
||||
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! { Size2D(self, other) [x, y] }
|
||||
implement_size! { Length(self, other) [points] }
|
||||
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::{FontProvider, FontIndex, FontDescriptor};
|
||||
|
||||
use crate::problem::Problems;
|
||||
use crate::diagnostic::Diagnostics;
|
||||
use crate::layout::MultiLayout;
|
||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseState, parse};
|
||||
use crate::syntax::span::{Position, SpanVec, offset_spans};
|
||||
|
||||
use crate::syntax::{Decorations, SyntaxModel, Scope, ParseState, parse};
|
||||
use crate::syntax::span::{Offset, Pos};
|
||||
|
||||
/// Declare a module and reexport all its contents.
|
||||
macro_rules! pub_use_mod {
|
||||
@ -45,17 +44,17 @@ macro_rules! pub_use_mod {
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
#[macro_use]
|
||||
pub mod problem;
|
||||
pub mod diagnostic;
|
||||
pub mod export;
|
||||
#[macro_use]
|
||||
pub mod func;
|
||||
pub mod layout;
|
||||
pub mod library;
|
||||
pub mod size;
|
||||
pub mod length;
|
||||
pub mod paper;
|
||||
pub mod style;
|
||||
pub mod syntax;
|
||||
|
||||
|
||||
/// Transforms source code into typesetted layouts.
|
||||
///
|
||||
/// A typesetter can be configured through various methods.
|
||||
@ -112,7 +111,7 @@ impl Typesetter {
|
||||
|
||||
/// Parse source code into a syntax tree.
|
||||
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.
|
||||
@ -176,18 +175,18 @@ impl<T> Pass<T> {
|
||||
/// User feedback data accumulated during a compilation pass.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct Feedback {
|
||||
/// Problems in the source code.
|
||||
pub problems: Problems,
|
||||
/// Diagnostics in the source code.
|
||||
pub diagnostics: Diagnostics,
|
||||
/// Decorations of the source code for semantic syntax highlighting.
|
||||
pub decos: SpanVec<Decoration>,
|
||||
pub decorations: Decorations,
|
||||
}
|
||||
|
||||
impl Feedback {
|
||||
/// Create a new feedback instance without errors and decos.
|
||||
pub fn new() -> Feedback {
|
||||
Feedback {
|
||||
problems: vec![],
|
||||
decos: vec![],
|
||||
diagnostics: vec![],
|
||||
decorations: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,15 +198,15 @@ impl Feedback {
|
||||
|
||||
/// Add other feedback data to this feedback.
|
||||
pub fn extend(&mut self, other: Feedback) {
|
||||
self.problems.extend(other.problems);
|
||||
self.decos.extend(other.decos);
|
||||
self.diagnostics.extend(other.diagnostics);
|
||||
self.decorations.extend(other.decorations);
|
||||
}
|
||||
|
||||
/// Add more feedback whose spans are local and need to be offset by an
|
||||
/// `offset` to be correct in this feedback's context.
|
||||
pub fn extend_offset(&mut self, more: Feedback, offset: Position) {
|
||||
self.problems.extend(offset_spans(more.problems, offset));
|
||||
self.decos.extend(offset_spans(more.decos, offset));
|
||||
pub fn extend_offset(&mut self, more: Feedback, offset: Pos) {
|
||||
self.diagnostics.extend(more.diagnostics.offset(offset));
|
||||
self.decorations.extend(more.decorations.offset(offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
use toddle::query::{FontWeight, FontStyle};
|
||||
use crate::size::FSize;
|
||||
use crate::length::ScaleLength;
|
||||
use super::*;
|
||||
|
||||
|
||||
function! {
|
||||
/// `font.family`: Set the font family.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -13,17 +12,17 @@ function! {
|
||||
}
|
||||
|
||||
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())
|
||||
.collect();
|
||||
|
||||
let tuples: Vec<_> = header.args.key
|
||||
.get_all::<String, Tuple>(&mut f.problems)
|
||||
.get_all::<String, Tuple>(&mut f.diagnostics)
|
||||
.collect();
|
||||
|
||||
let classes = tuples.into_iter()
|
||||
.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())
|
||||
.collect();
|
||||
(class.to_lowercase(), fallback)
|
||||
@ -64,8 +63,8 @@ function! {
|
||||
parse(header, body, ctx, f) {
|
||||
FontStyleFunc {
|
||||
body: body!(opt: body, ctx, f),
|
||||
style: header.args.pos.get::<FontStyle>(&mut f.problems)
|
||||
.or_missing(&mut f.problems, header.name.span, "style"),
|
||||
style: header.args.pos.get::<FontStyle>(&mut f.diagnostics)
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "style"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +83,7 @@ function! {
|
||||
|
||||
parse(header, 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 }| {
|
||||
if is_clamped {
|
||||
warning!(
|
||||
@ -96,7 +95,7 @@ function! {
|
||||
|
||||
weight
|
||||
})
|
||||
.or_missing(&mut f.problems, header.name.span, "weight");
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "weight");
|
||||
|
||||
FontWeightFunc { body, weight }
|
||||
}
|
||||
@ -111,25 +110,25 @@ function! {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontSizeFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
size: Option<FSize>,
|
||||
size: Option<ScaleLength>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
FontSizeFunc {
|
||||
body: body!(opt: body, ctx, f),
|
||||
size: header.args.pos.get::<FSize>(&mut f.problems)
|
||||
.or_missing(&mut f.problems, header.name.span, "size")
|
||||
size: header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "size")
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, self.size, |t, s| {
|
||||
match s {
|
||||
FSize::Absolute(size) => {
|
||||
ScaleLength::Absolute(size) => {
|
||||
t.base_font_size = size;
|
||||
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::*;
|
||||
|
||||
|
||||
function! {
|
||||
/// `align`: Aligns content along the layouting axes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -13,14 +12,14 @@ function! {
|
||||
parse(header, body, ctx, f) {
|
||||
AlignFunc {
|
||||
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) {
|
||||
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))
|
||||
});
|
||||
|
||||
@ -61,14 +60,14 @@ function! {
|
||||
DirectionFunc {
|
||||
name_span: header.name.span,
|
||||
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) {
|
||||
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))
|
||||
});
|
||||
|
||||
@ -103,15 +102,15 @@ function! {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BoxFunc {
|
||||
body: SyntaxModel,
|
||||
extents: AxisMap<PSize>,
|
||||
extents: AxisMap<ScaleLength>,
|
||||
debug: Option<bool>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
BoxFunc {
|
||||
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
||||
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
||||
debug: header.args.key.get::<bool>(&mut f.problems, "debug"),
|
||||
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
|
||||
debug: header.args.key.get::<bool>(&mut f.diagnostics, "debug"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,12 +122,12 @@ function! {
|
||||
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] {
|
||||
if let Some(psize) = map.get(axis) {
|
||||
let size = psize.scaled(ctx.base.get(axis));
|
||||
*ctx.base.get_mut(axis) = size;
|
||||
*ctx.spaces[0].dimensions.get_mut(axis) = size;
|
||||
if let Some(scale) = map.get(axis) {
|
||||
let length = scale.scaled(ctx.base.get(axis));
|
||||
*ctx.base.get_mut(axis) = length;
|
||||
*ctx.spaces[0].dimensions.get_mut(axis) = length;
|
||||
*ctx.spaces[0].expansion.get_mut(axis) = true;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ pub_use_mod!(layout);
|
||||
pub_use_mod!(page);
|
||||
pub_use_mod!(spacing);
|
||||
|
||||
|
||||
/// Create a scope with all standard functions.
|
||||
pub fn std() -> Scope {
|
||||
let mut std = Scope::new::<ValFunc>();
|
||||
|
@ -1,23 +1,22 @@
|
||||
use crate::size::Size;
|
||||
use crate::style::{Paper, PaperClass};
|
||||
use crate::length::Length;
|
||||
use crate::paper::{Paper, PaperClass};
|
||||
use super::*;
|
||||
|
||||
|
||||
function! {
|
||||
/// `page.size`: Set the size of pages.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PageSizeFunc {
|
||||
paper: Option<Paper>,
|
||||
extents: AxisMap<Size>,
|
||||
extents: AxisMap<Length>,
|
||||
flip: bool,
|
||||
}
|
||||
|
||||
parse(header, body, state, f) {
|
||||
body!(nope: body, f);
|
||||
PageSizeFunc {
|
||||
paper: header.args.pos.get::<Paper>(&mut f.problems),
|
||||
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
||||
flip: header.args.key.get::<bool>(&mut f.problems, "flip").unwrap_or(false),
|
||||
paper: header.args.pos.get::<Paper>(&mut f.diagnostics),
|
||||
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
|
||||
flip: header.args.key.get::<bool>(&mut f.diagnostics, "flip").unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,12 +25,12 @@ function! {
|
||||
|
||||
if let Some(paper) = self.paper {
|
||||
style.class = paper.class;
|
||||
style.dimensions = paper.dimensions;
|
||||
style.dimensions = paper.size();
|
||||
} else {
|
||||
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(Vertical, |&height| style.dimensions.y = height);
|
||||
|
||||
@ -53,13 +52,13 @@ function! {
|
||||
parse(header, body, state, f) {
|
||||
body!(nope: body, f);
|
||||
PageMarginsFunc {
|
||||
padding: PaddingMap::parse(&mut f.problems, &mut header.args),
|
||||
padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
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)]
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::size::FSize;
|
||||
use crate::length::ScaleLength;
|
||||
use crate::layout::SpacingKind;
|
||||
|
||||
use super::*;
|
||||
use self::ContentKind::*;
|
||||
|
||||
|
||||
function! {
|
||||
/// `line.break`, `n`: Ends the current line.
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
@ -41,7 +40,7 @@ function! {
|
||||
pub struct ContentSpacingFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
content: ContentKind,
|
||||
spacing: Option<f32>,
|
||||
spacing: Option<f64>,
|
||||
}
|
||||
|
||||
type Meta = ContentKind;
|
||||
@ -50,9 +49,9 @@ function! {
|
||||
ContentSpacingFunc {
|
||||
body: body!(opt: body, state, f),
|
||||
content: meta,
|
||||
spacing: header.args.pos.get::<f64>(&mut f.problems)
|
||||
.map(|num| num as f32)
|
||||
.or_missing(&mut f.problems, header.name.span, "spacing"),
|
||||
spacing: header.args.pos.get::<f64>(&mut f.diagnostics)
|
||||
.map(|num| num as f64)
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +78,7 @@ function! {
|
||||
/// `spacing`, `h`, `v`: Adds spacing along an axis.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SpacingFunc {
|
||||
spacing: Option<(AxisKey, FSize)>,
|
||||
spacing: Option<(AxisKey, ScaleLength)>,
|
||||
}
|
||||
|
||||
type Meta = Option<SpecificAxis>;
|
||||
@ -88,11 +87,11 @@ function! {
|
||||
body!(nope: body, f);
|
||||
SpacingFunc {
|
||||
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))
|
||||
} else {
|
||||
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.problems)
|
||||
}.or_missing(&mut f.problems, header.name.span, "spacing"),
|
||||
header.args.key.get_with_key::<AxisKey, ScaleLength>(&mut f.diagnostics)
|
||||
}.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::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.
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
@ -25,35 +25,35 @@ pub struct TextStyle {
|
||||
/// whether the next `*` adds or removes font weight.
|
||||
pub bolder: bool,
|
||||
/// The base font size.
|
||||
pub base_font_size: Size,
|
||||
pub base_font_size: Length,
|
||||
/// 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).
|
||||
pub word_spacing_scale: f32,
|
||||
pub word_spacing_scale: f64,
|
||||
/// 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).
|
||||
pub paragraph_spacing_scale: f32,
|
||||
pub paragraph_spacing_scale: f64,
|
||||
}
|
||||
|
||||
impl TextStyle {
|
||||
/// The scaled font size.
|
||||
pub fn font_size(&self) -> Size {
|
||||
pub fn font_size(&self) -> Length {
|
||||
self.base_font_size * self.font_scale
|
||||
}
|
||||
|
||||
/// The absolute word spacing.
|
||||
pub fn word_spacing(&self) -> Size {
|
||||
pub fn word_spacing(&self) -> Length {
|
||||
self.word_spacing_scale * self.font_size()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ impl Default for TextStyle {
|
||||
weight: FontWeight(400),
|
||||
},
|
||||
bolder: false,
|
||||
base_font_size: Size::pt(11.0),
|
||||
base_font_size: Length::pt(11.0),
|
||||
font_scale: 1.0,
|
||||
word_spacing_scale: 0.25,
|
||||
line_spacing_scale: 1.2,
|
||||
@ -92,10 +92,10 @@ pub struct PageStyle {
|
||||
/// The class of this page.
|
||||
pub class: PaperClass,
|
||||
/// 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
|
||||
/// default for the paper class is used.
|
||||
pub margins: ValueBox<Option<PSize>>,
|
||||
pub margins: Value4<Option<ScaleLength>>,
|
||||
}
|
||||
|
||||
impl PageStyle {
|
||||
@ -103,17 +103,17 @@ impl PageStyle {
|
||||
pub fn new(paper: Paper) -> PageStyle {
|
||||
PageStyle {
|
||||
class: paper.class,
|
||||
dimensions: paper.dimensions,
|
||||
margins: ValueBox::with_all(None),
|
||||
dimensions: paper.size(),
|
||||
margins: Value4::with_all(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// The absolute margins.
|
||||
pub fn margins(&self) -> SizeBox {
|
||||
pub fn margins(&self) -> Margins {
|
||||
let dims = self.dimensions;
|
||||
let default = self.class.default_margins();
|
||||
|
||||
SizeBox {
|
||||
Margins {
|
||||
left: self.margins.left.unwrap_or(default.left).scaled(dims.x),
|
||||
top: self.margins.top.unwrap_or(default.top).scaled(dims.y),
|
||||
right: self.margins.right.unwrap_or(default.right).scaled(dims.x),
|
||||
@ -127,270 +127,3 @@ impl Default for PageStyle {
|
||||
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::u8;
|
||||
|
||||
use crate::problem::Problems;
|
||||
use crate::size::Size;
|
||||
use crate::diagnostic::Diagnostics;
|
||||
use crate::length::Length;
|
||||
use super::func::{Key, Value};
|
||||
use super::span::{Span, Spanned};
|
||||
use super::tokens::is_identifier;
|
||||
|
||||
|
||||
/// An argument or return value.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Expr {
|
||||
@ -22,8 +21,8 @@ pub enum Expr {
|
||||
Str(String),
|
||||
/// A number: `1.2, 200%`.
|
||||
Number(f64),
|
||||
/// A size: `2cm, 5.2in`.
|
||||
Size(Size),
|
||||
/// A length: `2cm, 5.2in`.
|
||||
Length(Length),
|
||||
/// A bool: `true, false`.
|
||||
Bool(bool),
|
||||
/// A color value, including the alpha channel: `#f79143ff`.
|
||||
@ -32,7 +31,7 @@ pub enum Expr {
|
||||
Tuple(Tuple),
|
||||
/// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`.
|
||||
NamedTuple(NamedTuple),
|
||||
/// An object: `{ fit: false, size: 12pt }`.
|
||||
/// An object: `{ fit: false, width: 12pt }`.
|
||||
Object(Object),
|
||||
/// An operator that negates the contained expression.
|
||||
Neg(Box<Spanned<Expr>>),
|
||||
@ -54,7 +53,7 @@ impl Expr {
|
||||
Ident(_) => "identifier",
|
||||
Str(_) => "string",
|
||||
Number(_) => "number",
|
||||
Size(_) => "size",
|
||||
Length(_) => "length",
|
||||
Bool(_) => "bool",
|
||||
Color(_) => "color",
|
||||
Tuple(_) => "tuple",
|
||||
@ -76,7 +75,7 @@ impl Debug for Expr {
|
||||
Ident(i) => i.fmt(f),
|
||||
Str(s) => s.fmt(f),
|
||||
Number(n) => n.fmt(f),
|
||||
Size(s) => s.fmt(f),
|
||||
Length(s) => s.fmt(f),
|
||||
Bool(b) => b.fmt(f),
|
||||
Color(c) => c.fmt(f),
|
||||
Tuple(t) => t.fmt(f),
|
||||
@ -128,7 +127,7 @@ impl Debug for Ident {
|
||||
///
|
||||
/// # Example
|
||||
/// ```typst
|
||||
/// [box: background=#423abaff]
|
||||
/// [page: background=#423abaff]
|
||||
/// ^^^^^^^^
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
@ -256,28 +255,28 @@ impl Tuple {
|
||||
}
|
||||
|
||||
/// Extract (and remove) the first matching value and remove and generate
|
||||
/// problems for all previous items that did not match.
|
||||
pub fn get<V: Value>(&mut self, problems: &mut Problems) -> Option<V> {
|
||||
/// diagnostics for all previous items that did not match.
|
||||
pub fn get<V: Value>(&mut self, diagnostics: &mut Diagnostics) -> Option<V> {
|
||||
while !self.items.is_empty() {
|
||||
let expr = self.items.remove(0);
|
||||
let span = expr.span;
|
||||
match V::parse(expr) {
|
||||
Ok(output) => return Some(output),
|
||||
Err(v) => problems.push(Spanned { v, span }),
|
||||
Err(v) => diagnostics.push(Spanned { v, span }),
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Extract and return an iterator over all values that match and generate
|
||||
/// problems for all items that do not match.
|
||||
pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems)
|
||||
/// diagnostics for all items that do not match.
|
||||
pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics)
|
||||
-> impl Iterator<Item=V> + 'a {
|
||||
self.items.drain(..).filter_map(move |expr| {
|
||||
let span = expr.span;
|
||||
match V::parse(expr) {
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// ```typst
|
||||
/// { fit: false, size: 12cm, items: (1, 2, 3) }
|
||||
/// { fit: false, width: 12cm, items: (1, 2, 3) }
|
||||
/// ```
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct Object {
|
||||
@ -398,9 +393,9 @@ impl Object {
|
||||
///
|
||||
/// Inserts an error if the value does not match. If the key is not
|
||||
/// 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)?;
|
||||
self.get_index::<V>(problems, index)
|
||||
self.get_index::<V>(diagnostics, index)
|
||||
}
|
||||
|
||||
/// Extract (and remove) a pair with a matching key and value.
|
||||
@ -409,12 +404,12 @@ impl Object {
|
||||
/// found, no error is inserted.
|
||||
pub fn get_with_key<K: Key, V: Value>(
|
||||
&mut self,
|
||||
problems: &mut Problems,
|
||||
diagnostics: &mut Diagnostics,
|
||||
) -> Option<(K, V)> {
|
||||
for (index, pair) in self.pairs.iter().enumerate() {
|
||||
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
|
||||
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
|
||||
@ -425,7 +420,7 @@ impl Object {
|
||||
/// Inserts errors for values that do not match.
|
||||
pub fn get_all<'a, K: Key, V: Value>(
|
||||
&'a mut self,
|
||||
problems: &'a mut Problems,
|
||||
diagnostics: &'a mut Diagnostics,
|
||||
) -> impl Iterator<Item=(K, V)> + 'a {
|
||||
let mut index = 0;
|
||||
std::iter::from_fn(move || {
|
||||
@ -434,7 +429,7 @@ impl Object {
|
||||
let key = Spanned { v: key.v.as_str(), span: key.span };
|
||||
|
||||
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 {
|
||||
index += 1;
|
||||
None
|
||||
@ -454,20 +449,20 @@ impl Object {
|
||||
/// ```
|
||||
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
|
||||
&'a mut self,
|
||||
problems: &'a mut Problems,
|
||||
diagnostics: &'a mut Diagnostics,
|
||||
) -> 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)))
|
||||
}
|
||||
|
||||
/// Extract the argument at the given index and insert an error if the value
|
||||
/// 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 span = expr.span;
|
||||
match V::parse(expr) {
|
||||
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::PaddingKey::*;
|
||||
|
||||
|
||||
/// Key types are used to extract keyword arguments from
|
||||
/// [`Objects`](crate::syntax::expr::Object). They represent the key part of a
|
||||
/// keyword argument.
|
||||
|
@ -1,18 +1,17 @@
|
||||
//! Deduplicating maps and keys for argument parsing.
|
||||
|
||||
use crate::problem::Problems;
|
||||
use crate::diagnostic::Diagnostics;
|
||||
use crate::layout::prelude::*;
|
||||
use crate::size::{PSize, ValueBox};
|
||||
use crate::length::{ScaleLength, Value4};
|
||||
use crate::syntax::span::Spanned;
|
||||
use super::keys::*;
|
||||
use super::values::*;
|
||||
use super::*;
|
||||
|
||||
|
||||
/// A map which deduplicates redundant arguments.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
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)>> {
|
||||
let mut map = DedupMap::new();
|
||||
map.extend(problems, iter);
|
||||
map.extend(diagnostics, iter);
|
||||
map
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
problems.push(error!(entry.span, "duplicate argument"));
|
||||
diagnostics.push(error!(entry.span, "duplicate argument"));
|
||||
} else {
|
||||
self.map.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)>> {
|
||||
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
|
||||
/// values. When the mapping introduces new duplicates, problems are
|
||||
/// values. When the mapping introduces new duplicates, diagnostics are
|
||||
/// 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 {
|
||||
let mut map = DedupMap::new();
|
||||
|
||||
for Spanned { v: (key, value), span } in self.map.iter() {
|
||||
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
|
||||
@ -98,21 +97,21 @@ pub struct AxisMap<V>(DedupMap<AxisKey, V>);
|
||||
impl<V: Value> AxisMap<V> {
|
||||
/// Parse an axis map from the object.
|
||||
pub fn parse<K>(
|
||||
problems: &mut Problems,
|
||||
diagnostics: &mut Diagnostics,
|
||||
object: &mut Object,
|
||||
) -> AxisMap<V> where K: Key + Into<AxisKey> {
|
||||
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)))
|
||||
.collect();
|
||||
|
||||
AxisMap(DedupMap::from_iter(problems, values))
|
||||
AxisMap(DedupMap::from_iter(diagnostics, values))
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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> {
|
||||
/// Parse a positional/axis map from the function arguments.
|
||||
pub fn parse<K>(
|
||||
problems: &mut Problems,
|
||||
diagnostics: &mut Diagnostics,
|
||||
args: &mut FuncArgs,
|
||||
) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
|
||||
let mut map = DedupMap::new();
|
||||
|
||||
for &key in &[PosAxisKey::First, PosAxisKey::Second] {
|
||||
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(problems) {
|
||||
map.insert(problems, Spanned { v: (key, v), span })
|
||||
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(diagnostics) {
|
||||
map.insert(diagnostics, Spanned { v: (key, v), span })
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
.collect();
|
||||
|
||||
map.extend(problems, keywords);
|
||||
map.extend(diagnostics, keywords);
|
||||
|
||||
PosAxisMap(map)
|
||||
}
|
||||
@ -149,7 +148,7 @@ impl<V: Value> PosAxisMap<V> {
|
||||
/// or specific axes to just generic axes.
|
||||
pub fn dedup<F>(
|
||||
&self,
|
||||
problems: &mut Problems,
|
||||
diagnostics: &mut Diagnostics,
|
||||
axes: LayoutAxes,
|
||||
mut f: F,
|
||||
) -> DedupMap<GenericAxis, V>
|
||||
@ -157,7 +156,7 @@ impl<V: Value> PosAxisMap<V> {
|
||||
F: FnMut(&V) -> Option<GenericAxis>,
|
||||
V: Clone,
|
||||
{
|
||||
self.0.dedup(problems, |key, val| {
|
||||
self.0.dedup(diagnostics, |key, val| {
|
||||
(match key {
|
||||
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
|
||||
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
|
||||
/// sides or single sides.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>);
|
||||
pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<ScaleLength>>);
|
||||
|
||||
impl PaddingMap {
|
||||
/// 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 all = args.pos.get::<Spanned<Defaultable<PSize>>>(problems);
|
||||
let all = args.pos.get::<Spanned<Defaultable<ScaleLength>>>(diagnostics);
|
||||
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
|
||||
.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())))
|
||||
.collect();
|
||||
|
||||
map.extend(problems, paddings);
|
||||
map.extend(diagnostics, paddings);
|
||||
|
||||
PaddingMap(map)
|
||||
}
|
||||
@ -196,13 +195,13 @@ impl PaddingMap {
|
||||
/// Apply the specified padding on a value box of optional, scalable sizes.
|
||||
pub fn apply(
|
||||
&self,
|
||||
problems: &mut Problems,
|
||||
diagnostics: &mut Diagnostics,
|
||||
axes: LayoutAxes,
|
||||
padding: &mut ValueBox<Option<PSize>>
|
||||
padding: &mut Value4<Option<ScaleLength>>
|
||||
) {
|
||||
use PaddingKey::*;
|
||||
|
||||
let map = self.0.dedup(problems, |key, &val| {
|
||||
let map = self.0.dedup(diagnostics, |key, &val| {
|
||||
(match key {
|
||||
All => All,
|
||||
Both(axis) => Both(axis.to_specific(axes)),
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Primitives for argument parsing in library functions.
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use crate::problem::{Problem, Problems};
|
||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||
use super::expr::{Expr, Ident, Tuple, Object, Pair};
|
||||
use super::span::{Span, Spanned};
|
||||
|
||||
@ -85,13 +85,13 @@ pub enum FuncArg {
|
||||
pub trait OptionExt: Sized {
|
||||
/// Add an error about a missing argument `arg` with the given span if the
|
||||
/// 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> {
|
||||
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() {
|
||||
problems.push(error!(span, "missing argument: {}", arg));
|
||||
diagnostics.push(error!(span, "missing argument: {}", arg));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -4,13 +4,12 @@ use std::fmt::{self, Display, Formatter};
|
||||
use toddle::query::{FontStyle, FontWeight};
|
||||
|
||||
use crate::layout::prelude::*;
|
||||
use crate::size::{Size, ScaleSize};
|
||||
use crate::style::Paper;
|
||||
use crate::length::{Length, ScaleLength};
|
||||
use crate::paper::Paper;
|
||||
use super::*;
|
||||
|
||||
use self::AlignmentValue::*;
|
||||
|
||||
|
||||
/// Value types are used to extract the values of positional and keyword
|
||||
/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and
|
||||
/// [`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:
|
||||
/// ```
|
||||
/// # use typstc::error;
|
||||
/// # use typstc::problem::Problem;
|
||||
/// # use typstc::diagnostic::Diagnostic;
|
||||
/// # use typstc::syntax::expr::Expr;
|
||||
/// # use typstc::syntax::func::Value;
|
||||
/// # use typstc::syntax::span::Spanned;
|
||||
/// # struct 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 {
|
||||
/// # /*
|
||||
/// Expr::Bool(b) => Ok(b),
|
||||
@ -44,11 +43,11 @@ use self::AlignmentValue::*;
|
||||
pub trait Value: Sized {
|
||||
/// Parse an expression into this value or return an error if the expression
|
||||
/// 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> {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||
let span = expr.span;
|
||||
V::parse(expr).map(|v| Spanned { v, span })
|
||||
}
|
||||
@ -58,7 +57,7 @@ impl<V: Value> Value for Spanned<V> {
|
||||
macro_rules! value {
|
||||
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
||||
impl Value for $type {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||
#[allow(unreachable_patterns)]
|
||||
match expr.v {
|
||||
$($p => Ok($r)),*,
|
||||
@ -77,13 +76,13 @@ value!(Ident, "identifier", Expr::Ident(i) => i);
|
||||
value!(String, "string", Expr::Str(s) => s);
|
||||
value!(f64, "number", Expr::Number(n) => n);
|
||||
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!(Object, "object", Expr::Object(o) => o);
|
||||
|
||||
value!(ScaleSize, "number or size",
|
||||
Expr::Size(size) => ScaleSize::Absolute(size),
|
||||
Expr::Number(scale) => ScaleSize::Scaled(scale as f32),
|
||||
value!(ScaleLength, "number or length",
|
||||
Expr::Length(length) => ScaleLength::Absolute(length),
|
||||
Expr::Number(scale) => ScaleLength::Scaled(scale as f64),
|
||||
);
|
||||
|
||||
/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements
|
||||
@ -108,20 +107,20 @@ impl From<StringLike> for String {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use typstc::syntax::func::{FuncArgs, Defaultable};
|
||||
/// # use typstc::size::Size;
|
||||
/// # use typstc::length::Length;
|
||||
/// # let mut args = FuncArgs::new();
|
||||
/// # let mut errors = vec![];
|
||||
/// args.key.get::<Defaultable<Size>>(&mut errors, "size");
|
||||
/// args.key.get::<Defaultable<Length>>(&mut errors, "length");
|
||||
/// ```
|
||||
/// This will yield.
|
||||
/// ```typst
|
||||
/// [func: size=default] => None
|
||||
/// [func: size=2cm] => Some(Size::cm(2.0))
|
||||
/// [func: length=default] => None
|
||||
/// [func: length=2cm] => Some(Length::cm(2.0))
|
||||
/// ```
|
||||
pub struct Defaultable<V>(pub Option<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 {
|
||||
Expr::Ident(ident) if ident.as_str() == "default" => None,
|
||||
_ => Some(V::parse(expr)?)
|
||||
@ -136,7 +135,7 @@ impl<V> From<Defaultable<V>> for Option<V> {
|
||||
}
|
||||
|
||||
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())
|
||||
.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
|
||||
/// 100 - 900 to make it a valid font weight.
|
||||
impl Value for (FontWeight, bool) {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||
match expr.v {
|
||||
Expr::Number(weight) => {
|
||||
let weight = weight.round();
|
||||
@ -170,14 +169,14 @@ impl Value for (FontWeight, bool) {
|
||||
}
|
||||
|
||||
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())
|
||||
.ok_or_else(|| error!("invalid paper type"))
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
"left-to-right" | "ltr" | "LTR" => LeftToRight,
|
||||
"right-to-left" | "rtl" | "RTL" => RightToLeft,
|
||||
@ -250,7 +249,7 @@ impl 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() {
|
||||
"origin" => Align(Origin),
|
||||
"center" => Align(Center),
|
||||
|
@ -20,7 +20,6 @@ pub_use_mod!(scope);
|
||||
pub_use_mod!(parsing);
|
||||
pub_use_mod!(tokens);
|
||||
|
||||
|
||||
/// Represents a parsed piece of source that can be layouted and in the future
|
||||
/// also be queried for information used for refactorings, autocomplete, etc.
|
||||
#[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.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -110,7 +112,6 @@ pub enum Decoration {
|
||||
/// ^^^^^^
|
||||
/// ```
|
||||
InvalidFuncName,
|
||||
|
||||
/// A key of a keyword argument:
|
||||
/// ```typst
|
||||
/// [box: width=5cm]
|
||||
@ -123,7 +124,6 @@ pub enum Decoration {
|
||||
/// ^^^^ ^^^^^
|
||||
/// ```
|
||||
ObjectKey,
|
||||
|
||||
/// An italic word.
|
||||
Italic,
|
||||
/// A bold word.
|
||||
|
@ -4,9 +4,12 @@ use std::str::FromStr;
|
||||
|
||||
use super::expr::*;
|
||||
use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg};
|
||||
use super::span::{Position, Span, Spanned};
|
||||
use super::span::{Pos, Span, Spanned};
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// the start of the function as a whole as opposed to the start of the
|
||||
/// 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 feedback = Feedback::new();
|
||||
|
||||
@ -102,7 +105,7 @@ impl<'s> FuncParser<'s> {
|
||||
state,
|
||||
// Start at column 1 because the opening bracket is also part of
|
||||
// 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,
|
||||
body,
|
||||
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)
|
||||
} else {
|
||||
// Parse the body with the fallback parser even when the header is
|
||||
@ -186,7 +189,7 @@ impl<'s> FuncParser<'s> {
|
||||
self.skip_white();
|
||||
|
||||
let key = ident;
|
||||
self.feedback.decos.push(
|
||||
self.feedback.decorations.push(
|
||||
Spanned::new(Decoration::ArgumentKey, key.span)
|
||||
);
|
||||
|
||||
@ -325,7 +328,7 @@ impl FuncParser<'_> {
|
||||
}
|
||||
|
||||
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::ExprHex(s) => {
|
||||
if let Ok(color) = RgbaColor::from_str(s) {
|
||||
@ -423,7 +426,7 @@ impl FuncParser<'_> {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.feedback.decos.push(
|
||||
self.feedback.decorations.push(
|
||||
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) {
|
||||
self.eat();
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
self.expected_at(thing, pos)
|
||||
} else {
|
||||
@ -544,7 +547,7 @@ impl<'s> FuncParser<'s> {
|
||||
self.peek().is_none()
|
||||
}
|
||||
|
||||
fn pos(&self) -> Position {
|
||||
fn pos(&self) -> Pos {
|
||||
self.peeked.flatten()
|
||||
.map(|s| s.span.start)
|
||||
.unwrap_or_else(|| self.tokens.pos())
|
||||
@ -604,13 +607,13 @@ fn unescape_raw(raw: &str) -> Vec<String> {
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod tests {
|
||||
use crate::size::Size;
|
||||
use crate::length::Length;
|
||||
use super::super::test::{check, DebugFn};
|
||||
use super::super::func::Value;
|
||||
use super::*;
|
||||
|
||||
use Decoration::*;
|
||||
use Expr::{Number as Num, Size as Sz, Bool};
|
||||
use Expr::{Number as Num, Length as Len, Bool};
|
||||
use Node::{
|
||||
Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
|
||||
Parbreak, Linebreak,
|
||||
@ -625,7 +628,7 @@ mod tests {
|
||||
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>();
|
||||
scope.add::<DebugFn>("f");
|
||||
scope.add::<DebugFn>("n");
|
||||
@ -633,25 +636,25 @@ mod tests {
|
||||
scope.add::<DebugFn>("val");
|
||||
|
||||
let state = ParseState { scope };
|
||||
let pass = parse($source, Position::ZERO, &state);
|
||||
let pass = parse($source, Pos::ZERO, &state);
|
||||
|
||||
// Test model.
|
||||
let (exp, cmp) = span_vec![$($model)*];
|
||||
check($source, exp, pass.output.nodes, cmp);
|
||||
|
||||
// Test problems.
|
||||
let (exp, cmp) = span_vec![$($problems)*];
|
||||
// Test diagnostics.
|
||||
let (exp, cmp) = span_vec![$($diagnostics)*];
|
||||
let exp = exp.into_iter()
|
||||
.map(|s: Spanned<&str>| s.map(|e| e.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
let found = pass.feedback.problems.into_iter()
|
||||
let found = pass.feedback.diagnostics.into_iter()
|
||||
.map(|s| s.map(|e| e.message))
|
||||
.collect::<Vec<_>>();
|
||||
check($source, exp, found, cmp);
|
||||
|
||||
// Test 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 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 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)) }
|
||||
@ -878,8 +880,8 @@ mod tests {
|
||||
pval!("name" => (Id("name")));
|
||||
pval!("\"hi\"" => (Str("hi")));
|
||||
pval!("3.14" => (Num(3.14)));
|
||||
pval!("4.5cm" => (Sz(Size::cm(4.5))));
|
||||
pval!("12e1pt" => (Pt(12e1)));
|
||||
pval!("4.5cm" => (Len(Length::cm(4.5))));
|
||||
pval!("12e1pt" => (Len(Length::pt(12e1))));
|
||||
pval!("#f7a20500" => (ColorStr("f7a20500")));
|
||||
pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string")));
|
||||
|
||||
@ -890,10 +892,10 @@ mod tests {
|
||||
pval!("(hi)" => (Id("hi")));
|
||||
|
||||
// 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!("(3mm * 2)" => (Mul(Sz(Size::mm(3.0)), Num(2.0))));
|
||||
pval!("12e-3cm/1pt" => (Div(Sz(Size::cm(12e-3)), Sz(Size::pt(1.0)))));
|
||||
pval!("(3mm * 2)" => (Mul(Len(Length::mm(3.0)), Num(2.0))));
|
||||
pval!("12e-3cm/1pt" => (Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0)))));
|
||||
|
||||
// Unclosed string.
|
||||
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
|
||||
@ -912,26 +914,24 @@ mod tests {
|
||||
fn parse_complex_mathematical_expressions() {
|
||||
// Valid expressions.
|
||||
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))
|
||||
)));
|
||||
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)
|
||||
)));
|
||||
|
||||
// Associativity of multiplication and division.
|
||||
p!("[val: 3/4*5]" =>
|
||||
[func!("val": (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))), {})]
|
||||
);
|
||||
pval!("3/4*5" => (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))));
|
||||
|
||||
// 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:6, 0:10, "missing right summand")
|
||||
]);
|
||||
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")],
|
||||
);
|
||||
}
|
||||
@ -976,7 +976,7 @@ mod tests {
|
||||
// Nested tuples.
|
||||
pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!(
|
||||
"css",
|
||||
Pt(1.0),
|
||||
Len(Length::pt(1.0)),
|
||||
named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)),
|
||||
Str("solid"),
|
||||
)));
|
||||
@ -1012,7 +1012,7 @@ mod tests {
|
||||
|
||||
// Missing key.
|
||||
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")]);
|
||||
|
||||
// Missing colon.
|
||||
@ -1053,7 +1053,7 @@ mod tests {
|
||||
Num(1.0),
|
||||
object!(
|
||||
"ab" => tuple!(),
|
||||
"d" => tuple!(Num(3.0), Pt(14.0)),
|
||||
"d" => tuple!(Num(3.0), Len(Length::pt(14.0))),
|
||||
),
|
||||
),
|
||||
Bool(false),
|
||||
@ -1085,7 +1085,7 @@ mod tests {
|
||||
#[test]
|
||||
fn parse_multiple_mixed_arguments() {
|
||||
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)],
|
||||
);
|
||||
pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), });
|
||||
@ -1144,7 +1144,7 @@ mod tests {
|
||||
fn parse_invalid_commas() {
|
||||
// Missing commas.
|
||||
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")],
|
||||
);
|
||||
p!(r#"[val: _"s"]"# =>
|
||||
|
@ -3,16 +3,14 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use crate::Pass;
|
||||
use crate::func::ParseFunc;
|
||||
use super::func::FuncCall;
|
||||
use super::parsing::ParseState;
|
||||
use super::parsing::CallParser;
|
||||
use super::Model;
|
||||
|
||||
/// A map from identifiers to function parsers.
|
||||
pub struct Scope {
|
||||
parsers: HashMap<String, Box<Parser>>,
|
||||
fallback: Box<Parser>,
|
||||
parsers: HashMap<String, Box<CallParser>>,
|
||||
fallback: Box<CallParser>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
@ -22,7 +20,7 @@ impl Scope {
|
||||
where F: ParseFunc<Meta=()> + Model + 'static {
|
||||
Scope {
|
||||
parsers: HashMap::new(),
|
||||
fallback: parser::<F>(()),
|
||||
fallback: make_parser::<F>(()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,17 +41,17 @@ impl Scope {
|
||||
where F: ParseFunc + Model + 'static {
|
||||
self.parsers.insert(
|
||||
name.to_string(),
|
||||
parser::<F>(metadata),
|
||||
make_parser::<F>(metadata),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Return the fallback parser.
|
||||
pub fn get_fallback_parser(&self) -> &Parser {
|
||||
pub fn get_fallback_parser(&self) -> &CallParser {
|
||||
&*self.fallback
|
||||
}
|
||||
}
|
||||
@ -66,11 +64,7 @@ impl Debug for Scope {
|
||||
}
|
||||
}
|
||||
|
||||
/// A function which parses the source of a function into a model type which
|
||||
/// implements [`Model`].
|
||||
type Parser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
|
||||
|
||||
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
|
||||
fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<CallParser>
|
||||
where F: ParseFunc + Model + 'static {
|
||||
Box::new(move |f, s| {
|
||||
F::parse(f, s, metadata.clone())
|
||||
|
@ -4,16 +4,22 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{Add, Sub};
|
||||
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`.
|
||||
pub type SpanVec<T> = Vec<Spanned<T>>;
|
||||
|
||||
/// [Offset](Span::offset) all spans in a vector of spanned things by a start
|
||||
/// position.
|
||||
pub fn offset_spans<T>(
|
||||
vec: SpanVec<T>,
|
||||
start: Position,
|
||||
) -> impl Iterator<Item=Spanned<T>> {
|
||||
vec.into_iter().map(move |s| s.map_span(|span| span.offset(start)))
|
||||
impl<T> Offset for SpanVec<T> {
|
||||
fn offset(mut self, by: Pos) -> Self {
|
||||
for spanned in &mut self {
|
||||
spanned.span = spanned.span.offset(by);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.v.fmt(f)?;
|
||||
@ -68,20 +80,25 @@ impl<T: Debug> Debug for Spanned<T> {
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Span {
|
||||
/// The inclusive start position.
|
||||
pub start: Position,
|
||||
pub start: Pos,
|
||||
/// The inclusive end position.
|
||||
pub end: Position,
|
||||
pub end: Pos,
|
||||
}
|
||||
|
||||
impl 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.
|
||||
pub fn new(start: Position, end: Position) -> Span {
|
||||
pub fn new(start: Pos, end: Pos) -> Span {
|
||||
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.
|
||||
pub fn merge(a: Span, b: 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.
|
||||
pub fn expand(&mut self, other: Span) {
|
||||
*self = Span::merge(*self, other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Offset a span by a start position.
|
||||
///
|
||||
/// This is, for example, used to translate error spans from function local
|
||||
/// to global.
|
||||
pub fn offset(self, start: Position) -> Span {
|
||||
impl Offset for Span {
|
||||
fn offset(self, by: Pos) -> Self {
|
||||
Span {
|
||||
start: start + self.start,
|
||||
end: start + self.end,
|
||||
start: self.start.offset(by),
|
||||
end: self.end.offset(by),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,34 +130,40 @@ impl Debug for Span {
|
||||
|
||||
/// Zero-indexed line-column position in source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||
pub struct Position {
|
||||
pub struct Pos {
|
||||
/// The zero-indexed line.
|
||||
pub line: usize,
|
||||
/// The zero-indexed column.
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
impl Pos {
|
||||
/// 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.
|
||||
pub fn new(line: usize, column: usize) -> Position {
|
||||
Position { line, column }
|
||||
pub fn new(line: usize, column: usize) -> Pos {
|
||||
Pos { line, column }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Position;
|
||||
impl Offset for Pos {
|
||||
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 {
|
||||
Position {
|
||||
Pos {
|
||||
line: self.line,
|
||||
column: self.column + rhs.column
|
||||
}
|
||||
} else {
|
||||
Position {
|
||||
Pos {
|
||||
line: self.line + rhs.line,
|
||||
column: rhs.column,
|
||||
}
|
||||
@ -155,17 +171,17 @@ impl Add for Position {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Position {
|
||||
type Output = Position;
|
||||
impl Sub for Pos {
|
||||
type Output = Pos;
|
||||
|
||||
fn sub(self, rhs: Position) -> Position {
|
||||
fn sub(self, rhs: Pos) -> Pos {
|
||||
if self.line == rhs.line {
|
||||
Position {
|
||||
Pos {
|
||||
line: 0,
|
||||
column: self.column - rhs.column
|
||||
}
|
||||
} else {
|
||||
Position {
|
||||
Pos {
|
||||
line: self.line - rhs.line,
|
||||
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 {
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ macro_rules! span_vec {
|
||||
|
||||
macro_rules! span_item {
|
||||
(($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 {
|
||||
span: Span::new(
|
||||
Position::new($sl, $sc),
|
||||
Position::new($el, $ec)
|
||||
Pos::new($sl, $sc),
|
||||
Pos::new($el, $ec)
|
||||
),
|
||||
v: $v
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
use unicode_xid::UnicodeXID;
|
||||
|
||||
use crate::size::Size;
|
||||
use super::span::{Position, Span, Spanned};
|
||||
use crate::length::Length;
|
||||
use super::span::{Pos, Span, Spanned};
|
||||
|
||||
use Token::*;
|
||||
use TokenMode::*;
|
||||
@ -73,8 +73,8 @@ pub enum Token<'s> {
|
||||
},
|
||||
/// A number in a function header: `3.14`.
|
||||
ExprNumber(f64),
|
||||
/// A size in a function header: `12pt`.
|
||||
ExprSize(Size),
|
||||
/// A length in a function header: `12pt`.
|
||||
ExprLength(Length),
|
||||
/// A boolean in a function header: `true | false`.
|
||||
ExprBool(bool),
|
||||
/// A hex value in a function header: `#20d82a`.
|
||||
@ -129,7 +129,7 @@ impl<'s> Token<'s> {
|
||||
ExprIdent(_) => "identifier",
|
||||
ExprStr { .. } => "string",
|
||||
ExprNumber(_) => "number",
|
||||
ExprSize(_) => "size",
|
||||
ExprLength(_) => "length",
|
||||
ExprBool(_) => "bool",
|
||||
ExprHex(_) => "hex value",
|
||||
Plus => "plus",
|
||||
@ -153,7 +153,7 @@ pub struct Tokens<'s> {
|
||||
src: &'s str,
|
||||
mode: TokenMode,
|
||||
iter: Peekable<Chars<'s>>,
|
||||
position: Position,
|
||||
pos: Pos,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
@ -172,12 +172,12 @@ impl<'s> Tokens<'s> {
|
||||
///
|
||||
/// The first token's span starts an the given `offset` position instead of
|
||||
/// the zero position.
|
||||
pub fn new(src: &'s str, offset: Position, mode: TokenMode) -> Tokens<'s> {
|
||||
pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Tokens<'s> {
|
||||
Tokens {
|
||||
src,
|
||||
mode,
|
||||
iter: src.chars().peekable(),
|
||||
position: offset,
|
||||
pos: offset,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
@ -190,8 +190,8 @@ impl<'s> Tokens<'s> {
|
||||
|
||||
/// The line-colunn position in the source at which the last token ends and
|
||||
/// next token will start.
|
||||
pub fn pos(&self) -> Position {
|
||||
self.position
|
||||
pub fn pos(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,14 +315,14 @@ impl<'s> Tokens<'s> {
|
||||
}, 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);
|
||||
let end = self.pos();
|
||||
|
||||
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);
|
||||
self.eat();
|
||||
|
||||
@ -354,7 +354,7 @@ impl<'s> Tokens<'s> {
|
||||
|
||||
self.eat();
|
||||
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_block_comment(); }
|
||||
'"' if mode == Header => { self.read_string(); }
|
||||
@ -427,8 +427,8 @@ impl<'s> Tokens<'s> {
|
||||
ExprNumber(num)
|
||||
} else if let Some(num) = parse_percentage(text) {
|
||||
ExprNumber(num / 100.0)
|
||||
} else if let Ok(size) = text.parse::<Size>() {
|
||||
ExprSize(size)
|
||||
} else if let Ok(length) = text.parse::<Length>() {
|
||||
ExprLength(length)
|
||||
} else if is_identifier(text) {
|
||||
ExprIdent(text)
|
||||
} else {
|
||||
@ -476,10 +476,10 @@ impl<'s> Tokens<'s> {
|
||||
self.index += c.len_utf8();
|
||||
|
||||
if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) {
|
||||
self.position.line += 1;
|
||||
self.position.column = 0;
|
||||
self.pos.line += 1;
|
||||
self.pos.column = 0;
|
||||
} else {
|
||||
self.position.column += 1;
|
||||
self.pos.column += 1;
|
||||
}
|
||||
|
||||
Some(c)
|
||||
@ -543,7 +543,7 @@ mod tests {
|
||||
LeftBrace as LB, RightBrace as RB,
|
||||
ExprIdent as Id,
|
||||
ExprNumber as Num,
|
||||
ExprSize as Sz,
|
||||
ExprLength as Len,
|
||||
ExprBool as Bool,
|
||||
ExprHex as Hex,
|
||||
Text as T,
|
||||
@ -557,7 +557,7 @@ mod tests {
|
||||
macro_rules! t {
|
||||
($mode:expr, $source:expr => [$($tokens:tt)*]) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -640,13 +640,13 @@ mod tests {
|
||||
t!(Header, "__main__" => [Id("__main__")]);
|
||||
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, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Sz(Size::pt(12.0))]);
|
||||
t!(Header, "1e5in" => [Sz(Size::inches(100000.0))]);
|
||||
t!(Header, "2.3cm" => [Sz(Size::cm(2.3))]);
|
||||
t!(Header, "12e-3in" => [Sz(Size::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, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0))]);
|
||||
t!(Header, "1e5in" => [Len(Length::inches(100000.0))]);
|
||||
t!(Header, "2.3cm" => [Len(Length::cm(2.3))]);
|
||||
t!(Header, "12e-3in" => [Len(Length::inches(12e-3))]);
|
||||
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, "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, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]);
|
||||
t!(Header, "{abc}" => [LB, Id("abc"), RB]);
|
||||
|
@ -12,13 +12,13 @@ use futures_executor::block_on;
|
||||
|
||||
use typstc::{Typesetter, DebugErrorProvider};
|
||||
use typstc::layout::MultiLayout;
|
||||
use typstc::size::{Size, Size2D, ValueBox};
|
||||
use typstc::style::{PageStyle, PaperClass};
|
||||
use typstc::length::{Length, Size, Value4};
|
||||
use typstc::style::PageStyle;
|
||||
use typstc::paper::PaperClass;
|
||||
use typstc::export::pdf;
|
||||
use toddle::query::FontIndex;
|
||||
use toddle::query::fs::EagerFsProvider;
|
||||
|
||||
|
||||
type DynResult<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
fn main() -> DynResult<()> {
|
||||
@ -72,8 +72,8 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
||||
|
||||
typesetter.set_page_style(PageStyle {
|
||||
class: PaperClass::Custom,
|
||||
dimensions: Size2D::with_all(Size::pt(250.0)),
|
||||
margins: ValueBox::with_all(None),
|
||||
dimensions: Size::with_all(Length::pt(250.0)),
|
||||
margins: Value4::with_all(None),
|
||||
});
|
||||
|
||||
let layouts = compile(&typesetter, src);
|
||||
@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
||||
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
||||
if cfg!(debug_assertions) {
|
||||
let typeset = block_on(typesetter.typeset(src));
|
||||
let problems = typeset.feedback.problems;
|
||||
let diagnostics = typeset.feedback.diagnostics;
|
||||
|
||||
if !problems.is_empty() {
|
||||
for problem in problems {
|
||||
if !diagnostics.is_empty() {
|
||||
for diagnostic in diagnostics {
|
||||
println!(" {:?} {:?}: {}",
|
||||
problem.v.severity,
|
||||
problem.span,
|
||||
problem.v.message
|
||||
diagnostic.v.level,
|
||||
diagnostic.span,
|
||||
diagnostic.v.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user