Better Debug/Display and Derives 🧽
This commit is contained in:
parent
40ea35cbe7
commit
3150fd5643
@ -21,7 +21,7 @@ pub struct Error {
|
||||
}
|
||||
|
||||
/// How severe / important an error is.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||
pub enum Severity {
|
||||
/// Something in the code is not good.
|
||||
Warning,
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Exporting of layouts into _PDF_ documents.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
|
||||
@ -74,8 +76,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
font_loader: &GlobalFontLoader,
|
||||
target: W,
|
||||
) -> PdfResult<PdfExporter<'a, W>> {
|
||||
let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?;
|
||||
let offsets = Self::calculate_offsets(layouts.len(), fonts.len());
|
||||
let (fonts, font_remap) = subset_fonts(layouts, font_loader)?;
|
||||
let offsets = calculate_offsets(layouts.len(), fonts.len());
|
||||
|
||||
Ok(PdfExporter {
|
||||
writer: PdfWriter::new(target),
|
||||
@ -86,95 +88,6 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Subsets all fonts and assign a new PDF-internal index to each one. The
|
||||
/// returned hash map maps the old indices (used by the layouts) to the new
|
||||
/// one used in the PDF. The new ones index into the returned vector of
|
||||
/// owned fonts.
|
||||
fn subset_fonts(
|
||||
layouts: &'a MultiLayout,
|
||||
font_loader: &GlobalFontLoader,
|
||||
) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
|
||||
let mut fonts = Vec::new();
|
||||
let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
|
||||
let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
|
||||
let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
|
||||
let mut active_font = FontIndex::MAX;
|
||||
|
||||
// We want to find out which fonts are used at all and which chars are
|
||||
// used for those. We use this information to create subsetted fonts.
|
||||
for layout in layouts {
|
||||
for action in &layout.actions {
|
||||
match action {
|
||||
LayoutAction::WriteText(text) => {
|
||||
font_chars
|
||||
.entry(active_font)
|
||||
.or_insert_with(HashSet::new)
|
||||
.extend(text.chars());
|
||||
},
|
||||
|
||||
LayoutAction::SetFont(index, _) => {
|
||||
active_font = *index;
|
||||
|
||||
let next_id = old_to_new.len();
|
||||
let new_id = *old_to_new
|
||||
.entry(active_font)
|
||||
.or_insert(next_id);
|
||||
|
||||
new_to_old
|
||||
.entry(new_id)
|
||||
.or_insert(active_font);
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let num_fonts = old_to_new.len();
|
||||
let mut font_loader = font_loader.borrow_mut();
|
||||
|
||||
// All tables not listed here are dropped.
|
||||
let tables: Vec<_> = [
|
||||
b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
|
||||
b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
|
||||
].iter().map(|&s| Tag(*s)).collect();
|
||||
|
||||
// Do the subsetting.
|
||||
for index in 0 .. num_fonts {
|
||||
let old_index = new_to_old[&index];
|
||||
let font = font_loader.get_with_index(old_index);
|
||||
|
||||
let chars = font_chars[&old_index].iter().cloned();
|
||||
let subsetted = match font.subsetted(chars, tables.iter().copied()) {
|
||||
Ok(data) => Font::from_bytes(data)?,
|
||||
Err(_) => font.clone(),
|
||||
};
|
||||
|
||||
fonts.push(subsetted);
|
||||
}
|
||||
|
||||
Ok((fonts, old_to_new))
|
||||
}
|
||||
|
||||
/// We need to know in advance which IDs to use for which objects to
|
||||
/// cross-reference them. Therefore, we calculate the indices in the
|
||||
/// beginning.
|
||||
fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
|
||||
let catalog = 1;
|
||||
let page_tree = catalog + 1;
|
||||
let pages = (page_tree + 1, page_tree + layout_count as Ref);
|
||||
let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
|
||||
let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
|
||||
|
||||
Offsets {
|
||||
catalog,
|
||||
page_tree,
|
||||
pages,
|
||||
contents,
|
||||
fonts: font_offsets,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write everything (writing entry point).
|
||||
fn write(&mut self) -> PdfResult<usize> {
|
||||
self.writer.write_header(Version::new(1, 7))?;
|
||||
@ -395,12 +308,100 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Subsets all fonts and assign a new PDF-internal index to each one. The
|
||||
/// returned hash map maps the old indices (used by the layouts) to the new one
|
||||
/// used in the PDF. The new ones index into the returned vector of owned fonts.
|
||||
fn subset_fonts(
|
||||
layouts: &MultiLayout,
|
||||
font_loader: &GlobalFontLoader,
|
||||
) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
|
||||
let mut fonts = Vec::new();
|
||||
let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
|
||||
let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
|
||||
let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
|
||||
let mut active_font = FontIndex::MAX;
|
||||
|
||||
// We want to find out which fonts are used at all and which chars are used
|
||||
// for those. We use this information to create subsetted fonts.
|
||||
for layout in layouts {
|
||||
for action in &layout.actions {
|
||||
match action {
|
||||
LayoutAction::WriteText(text) => {
|
||||
font_chars
|
||||
.entry(active_font)
|
||||
.or_insert_with(HashSet::new)
|
||||
.extend(text.chars());
|
||||
},
|
||||
|
||||
LayoutAction::SetFont(index, _) => {
|
||||
active_font = *index;
|
||||
|
||||
let next_id = old_to_new.len();
|
||||
let new_id = *old_to_new
|
||||
.entry(active_font)
|
||||
.or_insert(next_id);
|
||||
|
||||
new_to_old
|
||||
.entry(new_id)
|
||||
.or_insert(active_font);
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let num_fonts = old_to_new.len();
|
||||
let mut font_loader = font_loader.borrow_mut();
|
||||
|
||||
// All tables not listed here are dropped.
|
||||
let tables: Vec<_> = [
|
||||
b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
|
||||
b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
|
||||
].iter().map(|&s| Tag(*s)).collect();
|
||||
|
||||
// Do the subsetting.
|
||||
for index in 0 .. num_fonts {
|
||||
let old_index = new_to_old[&index];
|
||||
let font = font_loader.get_with_index(old_index);
|
||||
|
||||
let chars = font_chars[&old_index].iter().cloned();
|
||||
let subsetted = match font.subsetted(chars, tables.iter().copied()) {
|
||||
Ok(data) => Font::from_bytes(data)?,
|
||||
Err(_) => font.clone(),
|
||||
};
|
||||
|
||||
fonts.push(subsetted);
|
||||
}
|
||||
|
||||
Ok((fonts, old_to_new))
|
||||
}
|
||||
|
||||
/// We need to know in advance which IDs to use for which objects to
|
||||
/// cross-reference them. Therefore, we calculate the indices in the beginning.
|
||||
fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
|
||||
let catalog = 1;
|
||||
let page_tree = catalog + 1;
|
||||
let pages = (page_tree + 1, page_tree + layout_count as Ref);
|
||||
let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
|
||||
let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
|
||||
|
||||
Offsets {
|
||||
catalog,
|
||||
page_tree,
|
||||
pages,
|
||||
contents,
|
||||
fonts: font_offsets,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an iterator from a reference pair.
|
||||
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
|
||||
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item=Ref> {
|
||||
start ..= end
|
||||
}
|
||||
|
||||
/// The error type for _PDF_ exporting.
|
||||
#[derive(Debug)]
|
||||
pub enum PdfExportError {
|
||||
/// An error occured while subsetting the font for the _PDF_.
|
||||
Font(LoadError),
|
||||
@ -408,17 +409,34 @@ pub enum PdfExportError {
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
error_type! {
|
||||
self: PdfExportError,
|
||||
res: PdfResult,
|
||||
show: f => match self {
|
||||
PdfExportError::Font(err) => err.fmt(f),
|
||||
PdfExportError::Io(err) => err.fmt(f),
|
||||
},
|
||||
source: match self {
|
||||
PdfExportError::Font(err) => Some(err),
|
||||
PdfExportError::Io(err) => Some(err),
|
||||
},
|
||||
from: (err: io::Error, PdfExportError::Io(err)),
|
||||
from: (err: LoadError, PdfExportError::Font(err)),
|
||||
type PdfResult<T> = Result<T, PdfExportError>;
|
||||
|
||||
impl Error for PdfExportError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match self {
|
||||
PdfExportError::Font(err) => Some(err),
|
||||
PdfExportError::Io(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PdfExportError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
PdfExportError::Font(err) => err.fmt(f),
|
||||
PdfExportError::Io(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadError> for PdfExportError {
|
||||
fn from(err: LoadError) -> PdfExportError {
|
||||
PdfExportError::Font(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for PdfExportError {
|
||||
fn from(err: io::Error) -> PdfExportError {
|
||||
PdfExportError::Io(err)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Drawing and configuration actions composing layouts.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use toddle::query::FontIndex;
|
||||
|
||||
use crate::size::{Size, Size2D};
|
||||
@ -11,7 +11,7 @@ use self::LayoutAction::*;
|
||||
|
||||
/// A layouting action, which is the basic building block layouts are composed
|
||||
/// of.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LayoutAction {
|
||||
/// Move to an absolute position.
|
||||
MoveAbsolute(Size2D),
|
||||
@ -34,20 +34,18 @@ impl Serialize for LayoutAction {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LayoutAction {
|
||||
impl Debug for LayoutAction {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use LayoutAction::*;
|
||||
match self {
|
||||
MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y),
|
||||
SetFont(i, s) => write!(f, "font {} {} {}", i.id, i.variant, s),
|
||||
SetFont(i, s) => write!(f, "font {}_{} {}", i.id, i.variant, s),
|
||||
WriteText(s) => write!(f, "write \"{}\"", s),
|
||||
DebugBox(s) => write!(f, "box {}", s),
|
||||
DebugBox(s) => write!(f, "box {} {}", s.x, s.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(LayoutAction);
|
||||
|
||||
/// A sequence of layouting actions.
|
||||
///
|
||||
/// The sequence of actions is optimized as the actions are added. For example,
|
||||
@ -60,7 +58,7 @@ debug_display!(LayoutAction);
|
||||
/// `add_layout` method, which allows a layout to be added at a position,
|
||||
/// effectively translating all movement actions inside the layout by the
|
||||
/// position.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LayoutActions {
|
||||
origin: Size2D,
|
||||
actions: Vec<LayoutAction>,
|
||||
|
@ -13,7 +13,7 @@ use super::*;
|
||||
|
||||
|
||||
/// Performs the line layouting.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct LineLayouter {
|
||||
/// The context for layouting.
|
||||
ctx: LineContext,
|
||||
@ -46,7 +46,7 @@ pub struct LineContext {
|
||||
/// A line run is a sequence of boxes with the same alignment that are arranged
|
||||
/// in a line. A real line can consist of multiple runs with different
|
||||
/// alignments.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
struct LineRun {
|
||||
/// The so-far accumulated layouts in the line.
|
||||
layouts: Vec<(Size, Layout)>,
|
||||
@ -102,7 +102,7 @@ impl LineLayouter {
|
||||
} else if layout.alignment.primary > alignment.primary {
|
||||
let mut rest_run = LineRun::new();
|
||||
|
||||
let usable = self.stack.usable().get_primary(axes);
|
||||
let usable = self.stack.usable().primary(axes);
|
||||
rest_run.usable = Some(match layout.alignment.primary {
|
||||
Alignment::Origin => unreachable!("origin > x"),
|
||||
Alignment::Center => usable - 2 * self.run.size.x,
|
||||
@ -228,7 +228,7 @@ impl LineLayouter {
|
||||
/// a function how much space it has to layout itself.
|
||||
pub fn remaining(&self) -> LayoutSpaces {
|
||||
let mut spaces = self.stack.remaining();
|
||||
*spaces[0].dimensions.get_secondary_mut(self.ctx.axes)
|
||||
*spaces[0].dimensions.secondary_mut(self.ctx.axes)
|
||||
-= self.run.size.y;
|
||||
spaces
|
||||
}
|
||||
@ -262,7 +262,7 @@ impl LineLayouter {
|
||||
true => offset,
|
||||
false => self.run.size.x
|
||||
- offset
|
||||
- layout.dimensions.get_primary(self.ctx.axes),
|
||||
- layout.dimensions.primary(self.ctx.axes),
|
||||
};
|
||||
|
||||
let pos = Size2D::with_x(x);
|
||||
|
@ -33,7 +33,7 @@ pub mod prelude {
|
||||
pub type MultiLayout = Vec<Layout>;
|
||||
|
||||
/// A finished box with content at fixed positions.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Layout {
|
||||
/// The size of the box.
|
||||
pub dimensions: Size2D,
|
||||
@ -91,7 +91,7 @@ impl Serialize for MultiLayout {
|
||||
pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
|
||||
|
||||
/// The space into which content is laid out.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct LayoutSpace {
|
||||
/// The maximum size of the box to layout in.
|
||||
pub dimensions: Size2D,
|
||||
|
@ -5,7 +5,6 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use smallvec::smallvec;
|
||||
use toddle::query::{SharedFontLoader, FontProvider};
|
||||
|
||||
use crate::GlobalFontLoader;
|
||||
use crate::error::Errors;
|
||||
@ -19,6 +18,7 @@ use super::*;
|
||||
|
||||
|
||||
/// Performs the model layouting.
|
||||
#[derive(Debug)]
|
||||
pub struct ModelLayouter<'a> {
|
||||
ctx: LayoutContext<'a>,
|
||||
layouter: LineLayouter,
|
||||
@ -52,6 +52,7 @@ pub struct LayoutContext<'a> {
|
||||
}
|
||||
|
||||
/// The result of layouting: Some layouted things and a list of errors.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Layouted<T> {
|
||||
/// The result of the layouting process.
|
||||
pub output: T,
|
||||
@ -63,7 +64,7 @@ pub struct Layouted<T> {
|
||||
pub type Commands<'a> = Vec<Command<'a>>;
|
||||
|
||||
/// Commands issued to the layouting engine by models.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Command<'a> {
|
||||
/// Layout the given model in the current context (i.e. not nested). The
|
||||
/// content of the model is not laid out into a separate box and then added,
|
||||
|
@ -27,7 +27,7 @@ use super::*;
|
||||
|
||||
|
||||
/// Performs the stack layouting.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct StackLayouter {
|
||||
/// The context for layouting.
|
||||
ctx: StackContext,
|
||||
@ -57,7 +57,7 @@ pub struct StackContext {
|
||||
|
||||
/// A layout space composed of subspaces which can have different axes and
|
||||
/// alignments.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
struct Space {
|
||||
/// The index of this space in the list of spaces.
|
||||
index: usize,
|
||||
@ -134,7 +134,7 @@ impl StackLayouter {
|
||||
// A hard space is simply an empty box.
|
||||
SpacingKind::Hard => {
|
||||
// Reduce the spacing such that it definitely fits.
|
||||
spacing.min_eq(self.space.usable.get_secondary(self.ctx.axes));
|
||||
spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
|
||||
let dimensions = Size2D::with_y(spacing);
|
||||
|
||||
self.update_metrics(dimensions);
|
||||
@ -179,7 +179,7 @@ impl StackLayouter {
|
||||
|
||||
self.space.size = size.specialized(axes);
|
||||
self.space.extra = extra.specialized(axes);
|
||||
*self.space.usable.get_secondary_mut(axes) -= dimensions.y;
|
||||
*self.space.usable.secondary_mut(axes) -= dimensions.y;
|
||||
}
|
||||
|
||||
/// Update the rulers to account for the new layout. Returns true if a
|
||||
@ -328,7 +328,7 @@ impl StackLayouter {
|
||||
// the usable space for following layouts at it's origin by its
|
||||
// extent along the secondary axis.
|
||||
*bound.get_mut(axes.secondary, Origin)
|
||||
+= axes.secondary.factor() * layout.dimensions.get_secondary(*axes);
|
||||
+= axes.secondary.factor() * layout.dimensions.secondary(*axes);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
|
@ -14,6 +14,7 @@ use super::*;
|
||||
|
||||
|
||||
/// Performs the text layouting.
|
||||
#[derive(Debug)]
|
||||
struct TextLayouter<'a> {
|
||||
ctx: TextContext<'a>,
|
||||
text: &'a str,
|
||||
@ -24,7 +25,7 @@ struct TextLayouter<'a> {
|
||||
}
|
||||
|
||||
/// The context for text layouting.
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TextContext<'a> {
|
||||
/// The font loader to retrieve fonts from when typesetting text
|
||||
/// using [`layout_text`].
|
||||
|
27
src/lib.rs
27
src/lib.rs
@ -16,14 +16,10 @@
|
||||
//! format is [_PDF_](crate::export::pdf). Alternatively, the layout can be
|
||||
//! serialized to pass it to a suitable renderer.
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
pub use toddle;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::io::Cursor;
|
||||
use async_trait::async_trait;
|
||||
use smallvec::smallvec;
|
||||
|
||||
@ -35,8 +31,15 @@ use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||
use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
|
||||
use crate::syntax::span::Position;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
/// Declare a module and reexport all its contents.
|
||||
macro_rules! pub_use_mod {
|
||||
($name:ident) => {
|
||||
mod $name;
|
||||
pub use $name::*;
|
||||
};
|
||||
}
|
||||
|
||||
pub mod error;
|
||||
pub mod export;
|
||||
#[macro_use]
|
||||
@ -51,6 +54,7 @@ pub mod syntax;
|
||||
/// Transforms source code into typesetted layouts.
|
||||
///
|
||||
/// A typesetter can be configured through various methods.
|
||||
#[derive(Debug)]
|
||||
pub struct Typesetter {
|
||||
/// The font loader shared by all typesetting processes.
|
||||
loader: GlobalFontLoader,
|
||||
@ -79,16 +83,16 @@ impl Typesetter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the base page style.
|
||||
pub fn set_page_style(&mut self, style: PageStyle) {
|
||||
self.style.page = style;
|
||||
}
|
||||
|
||||
/// Set the base text style.
|
||||
pub fn set_text_style(&mut self, style: TextStyle) {
|
||||
self.style.text = style;
|
||||
}
|
||||
|
||||
/// Set the base page style.
|
||||
pub fn set_page_style(&mut self, style: PageStyle) {
|
||||
self.style.page = style;
|
||||
}
|
||||
|
||||
/// A reference to the backing font loader.
|
||||
pub fn loader(&self) -> &GlobalFontLoader {
|
||||
&self.loader
|
||||
@ -134,6 +138,7 @@ impl Typesetter {
|
||||
/// Wraps a font provider and transforms its errors into boxed trait objects.
|
||||
/// This enables font providers that do not return boxed errors to be used with
|
||||
/// the typesetter.
|
||||
#[derive(Debug)]
|
||||
pub struct DynErrorProvider<P> {
|
||||
provider: P,
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! The _Typst_ standard library.
|
||||
|
||||
use toddle::query::FontProvider;
|
||||
use crate::syntax::Scope;
|
||||
use crate::func::prelude::*;
|
||||
|
||||
|
@ -67,7 +67,7 @@ function! {
|
||||
|
||||
/// The different kinds of content that can be spaced. Used as a metadata type
|
||||
/// for the [`ContentSpacingFunc`].
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ContentKind {
|
||||
Word,
|
||||
|
@ -1,74 +0,0 @@
|
||||
//! Auxiliary macros.
|
||||
|
||||
|
||||
/// Create trait implementations for an error type.
|
||||
macro_rules! error_type {
|
||||
(
|
||||
$this:ident: $type:ident,
|
||||
$(res: $res:ident,)*
|
||||
show: $f:ident => $show:expr,
|
||||
$(source: $source:expr,)*
|
||||
$(from: ($err:ident: $from:path, $conv:expr),)*
|
||||
) => {
|
||||
// Possibly create a result type.
|
||||
$(type $res<T> = std::result::Result<T, $type>;)*
|
||||
|
||||
impl std::fmt::Display for $type {
|
||||
fn fmt(&$this, $f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
$show
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!($type);
|
||||
|
||||
impl std::error::Error for $type {
|
||||
// The source method is only generated if an implementation was given.
|
||||
$(fn source(&$this) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
$source
|
||||
})*
|
||||
}
|
||||
|
||||
// Create any number of from implementations.
|
||||
$(impl From<$from> for $type {
|
||||
fn from($err: $from) -> $type {
|
||||
$conv
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
/// Create a `Debug` implementation from a `Display` implementation.
|
||||
macro_rules! debug_display {
|
||||
($type:ident) => (
|
||||
impl std::fmt::Debug for $type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
);
|
||||
($type:ident; $generics:tt where $($bounds:tt)*) => (
|
||||
impl<$generics> std::fmt::Debug for $type<$generics> where $($bounds)* {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Declare a module and reexport all its contents.
|
||||
macro_rules! pub_use_mod {
|
||||
($name:ident) => {
|
||||
mod $name;
|
||||
pub use $name::*;
|
||||
};
|
||||
}
|
||||
|
||||
/// Whether an expression matches a set of patterns.
|
||||
macro_rules! matches {
|
||||
($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => {
|
||||
match $expression {
|
||||
$( $pattern )|+ $( if $guard )? => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
92
src/size.rs
92
src/size.rs
@ -76,7 +76,11 @@ impl Display for Size {
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(Size);
|
||||
impl Debug for Size {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Size {
|
||||
type Output = Size;
|
||||
@ -115,12 +119,16 @@ impl Display for ScaleSize {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ScaleSize::Absolute(size) => write!(f, "{}", size),
|
||||
ScaleSize::Scaled(scale) => write!(f, "x{}", scale),
|
||||
ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(ScaleSize);
|
||||
impl Debug for ScaleSize {
|
||||
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;
|
||||
@ -129,7 +137,7 @@ pub type FSize = ScaleSize;
|
||||
pub type PSize = ScaleSize;
|
||||
|
||||
/// A value in two dimensions.
|
||||
#[derive(Default, Copy, Clone, PartialEq)]
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Value2D<T> {
|
||||
/// The horizontal component.
|
||||
pub x: T,
|
||||
@ -137,7 +145,7 @@ pub struct Value2D<T> {
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Copy> Value2D<T> {
|
||||
impl<T: Clone> Value2D<T> {
|
||||
/// Create a new 2D-value from two values.
|
||||
pub fn new(x: T, y: T) -> Value2D<T> { Value2D { x, y } }
|
||||
|
||||
@ -164,7 +172,7 @@ impl<T: Copy> Value2D<T> {
|
||||
}
|
||||
|
||||
/// 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, y: s } }
|
||||
pub fn with_all(s: T) -> Value2D<T> { Value2D { x: s.clone(), y: s } }
|
||||
|
||||
/// Get the specificed component.
|
||||
pub fn get(self, axis: SpecificAxis) -> T {
|
||||
@ -183,22 +191,22 @@ impl<T: Copy> Value2D<T> {
|
||||
}
|
||||
|
||||
/// Return the primary value of this specialized 2D-value.
|
||||
pub fn get_primary(self, axes: LayoutAxes) -> T {
|
||||
pub fn primary(self, axes: LayoutAxes) -> T {
|
||||
if axes.primary.axis() == Horizontal { self.x } else { self.y }
|
||||
}
|
||||
|
||||
/// Borrow the primary value of this specialized 2D-value mutably.
|
||||
pub fn get_primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||
if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y }
|
||||
}
|
||||
|
||||
/// Return the secondary value of this specialized 2D-value.
|
||||
pub fn get_secondary(self, axes: LayoutAxes) -> T {
|
||||
pub fn secondary(self, axes: LayoutAxes) -> T {
|
||||
if axes.primary.axis() == Horizontal { self.y } else { self.x }
|
||||
}
|
||||
|
||||
/// Borrow the secondary value of this specialized 2D-value mutably.
|
||||
pub fn get_secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||
if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x }
|
||||
}
|
||||
|
||||
@ -227,15 +235,12 @@ impl<T: Copy> Value2D<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for Value2D<T> where T: Display {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[{}, {}]", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Value2D<T> where T: Debug {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[{:?}, {:?}]", self.x, self.y)
|
||||
f.debug_list()
|
||||
.entry(&self.x)
|
||||
.entry(&self.y)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,6 +299,7 @@ impl Neg for Size2D {
|
||||
|
||||
/// 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)]
|
||||
pub struct StretchValue<T> {
|
||||
/// The minimum this value can be stretched to.
|
||||
pub min: T,
|
||||
@ -310,23 +316,11 @@ impl<T> StretchValue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for StretchValue<T> where T: Display {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {}, {})", self.min, self.opt, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for StretchValue<T> where T: Debug {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
/// A size that is stretchable.
|
||||
pub type StretchSize = StretchValue<Size>;
|
||||
|
||||
/// A value in four dimensions.
|
||||
#[derive(Default, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ValueBox<T> {
|
||||
/// The left extent.
|
||||
pub left: T,
|
||||
@ -389,20 +383,6 @@ impl<T: Clone> ValueBox<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for ValueBox<T> where T: Display {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
|
||||
self.left, self.top, self.right, self.bottom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for ValueBox<T> where T: Debug {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]",
|
||||
self.left, self.top, self.right, self.bottom)
|
||||
}
|
||||
}
|
||||
|
||||
/// A size in four dimensions.
|
||||
pub type SizeBox = ValueBox<Size>;
|
||||
|
||||
@ -416,14 +396,6 @@ impl SizeBox {
|
||||
};
|
||||
}
|
||||
|
||||
/// An error which can be returned when parsing a size.
|
||||
pub struct ParseSizeError;
|
||||
|
||||
error_type! {
|
||||
self: ParseSizeError,
|
||||
show: f => write!(f, "failed to parse size"),
|
||||
}
|
||||
|
||||
impl FromStr for Size {
|
||||
type Err = ParseSizeError;
|
||||
|
||||
@ -433,16 +405,28 @@ impl FromStr for Size {
|
||||
_ if src.ends_with("mm") => Size::mm,
|
||||
_ if src.ends_with("cm") => Size::cm,
|
||||
_ if src.ends_with("in") => Size::inches,
|
||||
_ => return Err(ParseSizeError),
|
||||
_ => return Err(ParseSizeError(())),
|
||||
};
|
||||
|
||||
Ok(func(src[..src.len() - 2]
|
||||
.parse::<f32>()
|
||||
.map_err(|_| ParseSizeError)?))
|
||||
.map_err(|_| ParseSizeError(()))?))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// An error which can be returned when parsing a size.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ParseSizeError(());
|
||||
|
||||
impl std::error::Error for ParseSizeError {}
|
||||
|
||||
impl Display for ParseSizeError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_str("invalid string for size")
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! implement_traits {
|
||||
($ty:ident, $t:ident, $o:ident
|
||||
reflexive {$(
|
||||
|
29
src/style.rs
29
src/style.rs
@ -6,16 +6,16 @@ use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
|
||||
|
||||
|
||||
/// Defines properties of pages and text.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct LayoutStyle {
|
||||
/// The style for pages.
|
||||
pub page: PageStyle,
|
||||
/// The style for text.
|
||||
pub text: TextStyle,
|
||||
/// The style for pages.
|
||||
pub page: PageStyle,
|
||||
}
|
||||
|
||||
/// Defines which fonts to use and how to space text.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TextStyle {
|
||||
/// A tree of font names and generic family names.
|
||||
pub fallback: FallbackTree,
|
||||
@ -87,7 +87,7 @@ impl Default for TextStyle {
|
||||
}
|
||||
|
||||
/// Defines the size and margins of a page.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct PageStyle {
|
||||
/// The class of this page.
|
||||
pub class: PaperClass,
|
||||
@ -159,12 +159,19 @@ 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 => ValueBox::with_all(PSize::Scaled(0.1)),
|
||||
Base => ValueBox::new(PSize::Scaled(0.119), PSize::Scaled(0.0569), PSize::Scaled(0.0476), PSize::Scaled(0.0569)),
|
||||
US => ValueBox::new(PSize::Scaled(0.176), PSize::Scaled(0.1092), PSize::Scaled(0.176), PSize::Scaled(0.091)),
|
||||
Newspaper => ValueBox::new(PSize::Scaled(0.0455), PSize::Scaled(0.0587), PSize::Scaled(0.0455), PSize::Scaled(0.0294)),
|
||||
Book => ValueBox::new(PSize::Scaled(0.12), PSize::Scaled(0.0852), PSize::Scaled(0.15), PSize::Scaled(0.0965)),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,7 +189,7 @@ macro_rules! papers {
|
||||
};
|
||||
|
||||
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
|
||||
#[doc = "Paper with the names `"]
|
||||
#[doc = "Paper with name `"]
|
||||
#[doc = $names]
|
||||
#[doc = "`."]
|
||||
pub const $var: Paper = Paper {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Expressions in function headers.
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use crate::error::Errors;
|
||||
use crate::size::Size;
|
||||
@ -44,6 +44,21 @@ impl Expr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use Expr::*;
|
||||
match self {
|
||||
Ident(i) => i.fmt(f),
|
||||
Str(s) => s.fmt(f),
|
||||
Number(n) => n.fmt(f),
|
||||
Size(s) => s.fmt(f),
|
||||
Bool(b) => b.fmt(f),
|
||||
Tuple(t) => t.fmt(f),
|
||||
Object(o) => o.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A unicode identifier.
|
||||
///
|
||||
/// The identifier must be valid! This is checked in [`Ident::new`] or
|
||||
@ -73,13 +88,19 @@ impl Ident {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Ident {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// An untyped sequence of expressions.
|
||||
///
|
||||
/// # Example
|
||||
/// ```typst
|
||||
/// (false, 12cm, "hi")
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct Tuple {
|
||||
/// The elements of the tuple.
|
||||
pub items: Vec<Spanned<Expr>>,
|
||||
@ -124,6 +145,16 @@ impl Tuple {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Tuple {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let mut tuple = f.debug_tuple("");
|
||||
for item in &self.items {
|
||||
tuple.field(item);
|
||||
}
|
||||
tuple.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A key-value collection of identifiers and associated expressions.
|
||||
///
|
||||
/// The pairs themselves are not spanned, but the combined spans can easily be
|
||||
@ -134,14 +165,14 @@ impl Tuple {
|
||||
/// ```typst
|
||||
/// { fit: false, size: 12cm, items: (1, 2, 3) }
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct Object {
|
||||
/// The key-value pairs of the object.
|
||||
pub pairs: Vec<Pair>,
|
||||
}
|
||||
|
||||
/// A key-value pair in an object.
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Pair {
|
||||
/// The key part.
|
||||
/// ```typst
|
||||
@ -247,73 +278,10 @@ impl Object {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
impl Debug for Object {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use Expr::*;
|
||||
match self {
|
||||
Ident(i) => write!(f, "{}", i),
|
||||
Str(s) => write!(f, "{:?}", s),
|
||||
Number(n) => write!(f, "{}", n),
|
||||
Size(s) => write!(f, "{}", s),
|
||||
Bool(b) => write!(f, "{}", b),
|
||||
Tuple(t) => write!(f, "{}", t),
|
||||
Object(o) => write!(f, "{}", o),
|
||||
}
|
||||
f.debug_map()
|
||||
.entries(self.pairs.iter().map(|p| (&p.key.v, &p.value.v)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ident {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Tuple {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
|
||||
let mut first = true;
|
||||
for item in &self.items {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", item.v)?;
|
||||
first = false;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Object {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if self.pairs.len() == 0 {
|
||||
return write!(f, "{{}}");
|
||||
}
|
||||
|
||||
write!(f, "{{ ")?;
|
||||
|
||||
let mut first = true;
|
||||
for pair in &self.pairs {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", pair)?;
|
||||
first = false;
|
||||
}
|
||||
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Pair {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}: {}", self.key.v, self.value.v)
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(Expr);
|
||||
debug_display!(Ident);
|
||||
debug_display!(Tuple);
|
||||
debug_display!(Object);
|
||||
debug_display!(Pair);
|
||||
|
@ -16,7 +16,7 @@ use super::*;
|
||||
/// list that needs to be passed to those functions.
|
||||
///
|
||||
/// All entries need to have span information to enable the error reporting.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct DedupMap<K, V> where K: Eq {
|
||||
map: Vec<Spanned<(K, V)>>,
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub trait Model: Debug + ModelBounds {
|
||||
}
|
||||
|
||||
/// A tree representation of source code.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct SyntaxModel {
|
||||
/// The syntactical elements making up this model.
|
||||
pub nodes: SpanVec<Node>,
|
||||
@ -97,7 +97,7 @@ impl PartialEq for Node {
|
||||
}
|
||||
|
||||
/// Decorations for semantic syntax highlighting.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Decoration {
|
||||
/// A valid function name:
|
||||
|
@ -18,6 +18,7 @@ pub struct ParseContext<'a> {
|
||||
|
||||
/// The result of parsing: Some parsed thing, errors and decorations for syntax
|
||||
/// highlighting.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Parsed<T> {
|
||||
/// The result of the parsing process.
|
||||
pub output: T,
|
||||
@ -321,9 +322,11 @@ impl<'s> FuncParser<'s> {
|
||||
|
||||
/// Skip all whitespace/comment tokens.
|
||||
fn skip_whitespace(&mut self) {
|
||||
self.eat_until(|t| !matches!(t,
|
||||
self.eat_until(|t| match t {
|
||||
Token::Space(_) | Token::LineComment(_) |
|
||||
Token::BlockComment(_)), false)
|
||||
Token::BlockComment(_) => false,
|
||||
_ => true,
|
||||
}, false)
|
||||
}
|
||||
|
||||
/// Add an error about an expected `thing` which was not found, showing
|
||||
|
@ -13,7 +13,7 @@ use super::Model;
|
||||
/// A map from identifiers to function parsers.
|
||||
pub struct Scope {
|
||||
parsers: HashMap<String, Box<Parser>>,
|
||||
fallback: Box<Parser>
|
||||
fallback: Box<Parser>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
@ -63,8 +63,9 @@ impl Scope {
|
||||
|
||||
impl Debug for Scope {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "Scope ")?;
|
||||
write!(f, "{:?}", self.parsers.keys())
|
||||
f.debug_set()
|
||||
.entries(self.parsers.keys())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Spans map elements to the part of source code they originate from.
|
||||
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{Add, Sub};
|
||||
use serde::Serialize;
|
||||
|
||||
@ -60,6 +60,12 @@ impl Sub for Position {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Position {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
/// Locates a slice of source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Span {
|
||||
@ -103,8 +109,14 @@ impl Span {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Span {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({:?} -> {:?})", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value with the span it corresponds to in the source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Spanned<T> {
|
||||
/// The value.
|
||||
pub v: T,
|
||||
@ -143,34 +155,3 @@ pub type SpanVec<T> = Vec<Spanned<T>>;
|
||||
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 Display for Position {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Span {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for Spanned<T> where T: std::fmt::Display {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {}, ", self.span.start, self.span.end)?;
|
||||
self.v.fmt(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Spanned<T> where T: std::fmt::Debug {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {}, ", self.span.start, self.span.end)?;
|
||||
self.v.fmt(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(Position);
|
||||
debug_display!(Span);
|
||||
|
@ -122,6 +122,7 @@ impl<'s> Token<'s> {
|
||||
}
|
||||
|
||||
/// An iterator over the tokens of a string of source code.
|
||||
#[derive(Debug)]
|
||||
pub struct Tokens<'s> {
|
||||
src: &'s str,
|
||||
mode: TokenizationMode,
|
||||
@ -133,7 +134,7 @@ pub struct Tokens<'s> {
|
||||
/// Whether to tokenize in header mode which yields expression, comma and
|
||||
/// similar tokens or in body mode which yields text and star, underscore,
|
||||
/// backtick tokens.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum TokenizationMode {
|
||||
Header,
|
||||
|
@ -52,7 +52,7 @@ fn main() -> DynResult<()> {
|
||||
for (name, src) in filtered {
|
||||
panic::catch_unwind(|| {
|
||||
if let Err(e) = test(&name, &src) {
|
||||
println!("error: {}", e);
|
||||
println!("error: {:?}", e);
|
||||
}
|
||||
}).ok();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user