summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-12-10 22:44:35 +0100
committerLaurenz <laurmaedje@gmail.com>2020-12-10 22:45:45 +0100
commit1cbd5f3051ba90b3f673bc2f6319192d05381719 (patch)
tree182134e9f355062a00a145fab3a988847c4ed13b
parentfdc1b378a3eb3cf325592b801c43e2ec2478ddff (diff)
Refine test infrastructure ✅
- Tests diagnostics - More and better separated image tests
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml16
-rw-r--r--bench/src/bench.rs2
-rw-r--r--src/geom/point.rs5
-rw-r--r--src/geom/size.rs5
-rw-r--r--src/main.rs6
-rw-r--r--src/parse/lines.rs71
-rw-r--r--src/parse/tests.rs8
-rw-r--r--tests/README.md7
-rw-r--r--tests/cmp/coma.pngbin79107 -> 0 bytes
-rw-r--r--tests/cmp/image.pngbin281515 -> 0 bytes
-rw-r--r--tests/ref/empty.pngbin0 -> 120 bytes
-rw-r--r--tests/ref/example-coma.pngbin0 -> 27418 bytes
-rw-r--r--tests/ref/image-error.pngbin0 -> 120 bytes
-rw-r--r--tests/ref/image-fit.pngbin0 -> 190100 bytes
-rw-r--r--tests/ref/image-jpeg.pngbin0 -> 69754 bytes
-rw-r--r--tests/ref/image-png.pngbin0 -> 37986 bytes
-rw-r--r--tests/typ/empty.typ0
-rw-r--r--tests/typ/example-coma.typ (renamed from tests/typ/coma.typ)2
-rw-r--r--tests/typ/image-error.typ8
-rw-r--r--tests/typ/image-fit.typ21
-rw-r--r--tests/typ/image-jpeg.typ2
-rw-r--r--tests/typ/image-png.typ2
-rw-r--r--tests/typ/image.typ15
-rw-r--r--tests/typeset.rs193
25 files changed, 240 insertions, 126 deletions
diff --git a/.gitignore b/.gitignore
index e4e7a1be..381556a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,5 @@
**/*.rs.bk
Cargo.lock
bench/target
-tests/png
-tests/pdf
+tests/out
_things
diff --git a/Cargo.toml b/Cargo.toml
index a32cc8ea..258afd0b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,26 +8,22 @@ edition = "2018"
members = ["bench"]
[features]
-default = ["fs", "anyhow"]
+default = ["cli", "fs"]
+cli = ["fs", "anyhow"]
fs = ["fontdock/fs"]
[dependencies]
-fontdock = { path = "../fontdock", default-features = false }
-pdf-writer = { path = "../pdf-writer" }
deflate = { version = "0.8.6" }
+fontdock = { path = "../fontdock", default-features = false }
image = { version = "0.23", default-features = false, features = ["jpeg", "png"] }
itoa = "0.4"
+pdf-writer = { path = "../pdf-writer" }
ttf-parser = "0.8.2"
unicode-xid = "0.2"
-
-# feature = "serde"
-serde = { version = "1", features = ["derive"], optional = true }
-
-# for the CLI
anyhow = { version = "1", optional = true }
+serde = { version = "1", features = ["derive"], optional = true }
[dev-dependencies]
-memmap = "0.7"
tiny-skia = "0.2"
[profile.dev]
@@ -38,7 +34,7 @@ lto = true
[[bin]]
name = "typst"
-required-features = ["fs", "anyhow"]
+required-features = ["cli"]
[[test]]
name = "typeset"
diff --git a/bench/src/bench.rs b/bench/src/bench.rs
index c232d4bf..947a02c5 100644
--- a/bench/src/bench.rs
+++ b/bench/src/bench.rs
@@ -13,7 +13,7 @@ use typst::parse::parse;
use typst::typeset;
const FONT_DIR: &str = "../fonts";
-const COMA: &str = include_str!("../../tests/typ/coma.typ");
+const COMA: &str = include_str!("../../tests/typ/example-coma.typ");
fn benchmarks(c: &mut Criterion) {
macro_rules! bench {
diff --git a/src/geom/point.rs b/src/geom/point.rs
index 10ab2d3a..4523a861 100644
--- a/src/geom/point.rs
+++ b/src/geom/point.rs
@@ -17,6 +17,11 @@ impl Point {
pub fn new(x: Length, y: Length) -> Self {
Self { x, y }
}
+
+ /// Create an instance with two equal components.
+ pub fn uniform(value: Length) -> Self {
+ Self { x: value, y: value }
+ }
}
impl Get<SpecAxis> for Point {
diff --git a/src/geom/size.rs b/src/geom/size.rs
index 0ad0e0f8..28984659 100644
--- a/src/geom/size.rs
+++ b/src/geom/size.rs
@@ -21,6 +21,11 @@ impl Size {
Self { width, height }
}
+ /// Create an instance with two equal components.
+ pub fn uniform(value: Length) -> Self {
+ Self { width: value, height: value }
+ }
+
/// Whether the other size fits into this one (smaller width and height).
pub fn fits(self, other: Self) -> bool {
self.width >= other.width && self.height >= other.height
diff --git a/src/main.rs b/src/main.rs
index 3f12655b..acd2d0cd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -59,10 +59,10 @@ fn main() -> anyhow::Result<()> {
let map = LineMap::new(&src);
for diag in diags {
let span = diag.span;
- let start = map.location(span.start);
- let end = map.location(span.end);
+ let start = map.location(span.start).unwrap();
+ let end = map.location(span.end).unwrap();
println!(
- " {}: {}:{}-{}: {}",
+ "{}: {}:{}-{}: {}",
diag.v.level,
src_path.display(),
start,
diff --git a/src/parse/lines.rs b/src/parse/lines.rs
index ce5a1fe5..be120d8a 100644
--- a/src/parse/lines.rs
+++ b/src/parse/lines.rs
@@ -1,7 +1,7 @@
//! Conversion of byte positions to line/column locations.
use super::Scanner;
-use crate::syntax::{Location, Pos};
+use crate::syntax::{Location, Offset, Pos};
/// Enables conversion of byte position to locations.
pub struct LineMap<'s> {
@@ -25,23 +25,48 @@ impl<'s> LineMap<'s> {
}
/// Convert a byte position to a location.
- ///
- /// # Panics
- /// This panics if the position is out of bounds.
- pub fn location(&self, pos: Pos) -> Location {
+ pub fn location(&self, pos: Pos) -> Option<Location> {
+ // Find the line which contains the position.
let line_index = match self.line_starts.binary_search(&pos) {
Ok(i) => i,
Err(i) => i - 1,
};
- let line_start = self.line_starts[line_index];
- let head = &self.src[line_start.to_usize() .. pos.to_usize()];
+ let start = self.line_starts.get(line_index)?;
+ let head = self.src.get(start.to_usize() .. pos.to_usize())?;
let column_index = head.chars().count();
- Location {
+ Some(Location {
line: 1 + line_index as u32,
column: 1 + column_index as u32,
- }
+ })
+ }
+
+ /// Convert a location to a byte position.
+ pub fn pos(&self, location: Location) -> Option<Pos> {
+ // Determine the boundaries of the line.
+ let line_idx = location.line.checked_sub(1)? as usize;
+ let line_start = self.line_starts.get(line_idx)?;
+ let line_end = self
+ .line_starts
+ .get(location.line as usize)
+ .map_or(self.src.len(), |pos| pos.to_usize());
+
+ let line = self.src.get(line_start.to_usize() .. line_end)?;
+
+ // Find the index in the line. For the first column, the index is always zero. For
+ // other columns, we have to look at which byte the char directly before the
+ // column in question ends. We can't do `nth(column_idx)` directly since the
+ // column may be behind the last char.
+ let column_idx = location.column.checked_sub(1)? as usize;
+ let line_offset = if let Some(prev_idx) = column_idx.checked_sub(1) {
+ let (idx, prev) = line.char_indices().nth(prev_idx)?;
+ idx + prev.len_utf8()
+ } else {
+ 0
+ };
+
+ Some(line_start.offset(Pos(line_offset as u32)))
}
}
@@ -71,18 +96,26 @@ mod tests {
#[test]
fn test_line_map_location() {
let map = LineMap::new(TEST);
- assert_eq!(map.location(Pos(0)), Location::new(1, 1));
- assert_eq!(map.location(Pos(2)), Location::new(1, 2));
- assert_eq!(map.location(Pos(6)), Location::new(1, 6));
- assert_eq!(map.location(Pos(7)), Location::new(2, 1));
- assert_eq!(map.location(Pos(8)), Location::new(2, 2));
- assert_eq!(map.location(Pos(12)), Location::new(2, 3));
- assert_eq!(map.location(Pos(21)), Location::new(4, 4));
+ assert_eq!(map.location(Pos(0)), Some(Location::new(1, 1)));
+ assert_eq!(map.location(Pos(2)), Some(Location::new(1, 2)));
+ assert_eq!(map.location(Pos(6)), Some(Location::new(1, 6)));
+ assert_eq!(map.location(Pos(7)), Some(Location::new(2, 1)));
+ assert_eq!(map.location(Pos(8)), Some(Location::new(2, 2)));
+ assert_eq!(map.location(Pos(12)), Some(Location::new(2, 3)));
+ assert_eq!(map.location(Pos(21)), Some(Location::new(4, 4)));
+ assert_eq!(map.location(Pos(22)), None);
}
#[test]
- #[should_panic]
- fn test_line_map_panics_out_of_bounds() {
- LineMap::new(TEST).location(Pos(22));
+ fn test_line_map_pos() {
+ fn assert_round_trip(map: &LineMap, pos: Pos) {
+ assert_eq!(map.location(pos).and_then(|loc| map.pos(loc)), Some(pos));
+ }
+
+ let map = LineMap::new(TEST);
+ assert_round_trip(&map, Pos(0));
+ assert_round_trip(&map, Pos(7));
+ assert_round_trip(&map, Pos(12));
+ assert_round_trip(&map, Pos(21));
}
}
diff --git a/src/parse/tests.rs b/src/parse/tests.rs
index 054b2cd9..172b1d15 100644
--- a/src/parse/tests.rs
+++ b/src/parse/tests.rs
@@ -56,24 +56,31 @@ use Unit::*;
fn Id(ident: &str) -> Expr {
Expr::Lit(Lit::Ident(Ident(ident.to_string())))
}
+
fn Bool(b: bool) -> Expr {
Expr::Lit(Lit::Bool(b))
}
+
fn Int(int: i64) -> Expr {
Expr::Lit(Lit::Int(int))
}
+
fn Float(float: f64) -> Expr {
Expr::Lit(Lit::Float(float))
}
+
fn Percent(percent: f64) -> Expr {
Expr::Lit(Lit::Percent(percent))
}
+
fn Length(val: f64, unit: Unit) -> Expr {
Expr::Lit(Lit::Length(val, unit))
}
+
fn Color(color: RgbaColor) -> Expr {
Expr::Lit(Lit::Color(color))
}
+
fn Str(string: &str) -> Expr {
Expr::Lit(Lit::Str(string.to_string()))
}
@@ -98,6 +105,7 @@ fn Unary(op: impl Into<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> Expr {
expr: expr.into().map(Box::new),
})
}
+
fn Binary(
op: impl Into<Spanned<BinOp>>,
lhs: impl Into<Spanned<Expr>>,
diff --git a/tests/README.md b/tests/README.md
index 89c31c89..7d9c3eda 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,8 +1,7 @@
# Tests
- `typ`: Input files
-- `pdf`: PDF files produced by tests
-- `png`: PNG files produced by tests
-- `cmp`: Reference images which the PNGs are compared to byte-wise to determine
- whether the test passed or failed
+- `ref`: Reference images which the output is compared with to determine
+ whether a test passed or failed
- `res`: Resource files used by tests
+- `out`: PNG and PDF files produced by tests
diff --git a/tests/cmp/coma.png b/tests/cmp/coma.png
deleted file mode 100644
index d0c524ec..00000000
--- a/tests/cmp/coma.png
+++ /dev/null
Binary files differ
diff --git a/tests/cmp/image.png b/tests/cmp/image.png
deleted file mode 100644
index 5bf744e9..00000000
--- a/tests/cmp/image.png
+++ /dev/null
Binary files differ
diff --git a/tests/ref/empty.png b/tests/ref/empty.png
new file mode 100644
index 00000000..812a3758
--- /dev/null
+++ b/tests/ref/empty.png
Binary files differ
diff --git a/tests/ref/example-coma.png b/tests/ref/example-coma.png
new file mode 100644
index 00000000..0c18b810
--- /dev/null
+++ b/tests/ref/example-coma.png
Binary files differ
diff --git a/tests/ref/image-error.png b/tests/ref/image-error.png
new file mode 100644
index 00000000..812a3758
--- /dev/null
+++ b/tests/ref/image-error.png
Binary files differ
diff --git a/tests/ref/image-fit.png b/tests/ref/image-fit.png
new file mode 100644
index 00000000..b89e78fd
--- /dev/null
+++ b/tests/ref/image-fit.png
Binary files differ
diff --git a/tests/ref/image-jpeg.png b/tests/ref/image-jpeg.png
new file mode 100644
index 00000000..ef9e74cb
--- /dev/null
+++ b/tests/ref/image-jpeg.png
Binary files differ
diff --git a/tests/ref/image-png.png b/tests/ref/image-png.png
new file mode 100644
index 00000000..4e0818d2
--- /dev/null
+++ b/tests/ref/image-png.png
Binary files differ
diff --git a/tests/typ/empty.typ b/tests/typ/empty.typ
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/typ/empty.typ
diff --git a/tests/typ/coma.typ b/tests/typ/example-coma.typ
index 839335b7..f841a122 100644
--- a/tests/typ/coma.typ
+++ b/tests/typ/example-coma.typ
@@ -1,3 +1,5 @@
+// Small integration test of syntax, page setup, box layout and alignment.
+
[page: width=450pt, height=300pt, margins=1cm]
[box][
diff --git a/tests/typ/image-error.typ b/tests/typ/image-error.typ
new file mode 100644
index 00000000..4fde4ab2
--- /dev/null
+++ b/tests/typ/image-error.typ
@@ -0,0 +1,8 @@
+// error: 5:9-5:30 failed to load image
+// error: 8:9-8:30 failed to load image
+
+// File does not exist.
+[image: "path/does/not/exist"]
+
+// File exists, but is no image.
+[image: "typ/image-error.typ"]
diff --git a/tests/typ/image-fit.typ b/tests/typ/image-fit.typ
new file mode 100644
index 00000000..b735f058
--- /dev/null
+++ b/tests/typ/image-fit.typ
@@ -0,0 +1,21 @@
+// Fit to width of page.
+[image: "res/rhino.png"]
+
+// Fit to height of page.
+[page: width=270pt][
+ [image: "res/rhino.png"]
+]
+
+// Set width explicitly.
+[image: "res/rhino.png", width=50pt]
+
+// Set height explicitly.
+[image: "res/rhino.png", height=50pt]
+
+// Set width and height explicitly and force stretching.
+[image: "res/rhino.png", width=25pt, height=50pt]
+
+// Make sure the bounding-box of the image is correct.
+[align: bottom, right][
+ [image: "res/tiger.jpg"]
+]
diff --git a/tests/typ/image-jpeg.typ b/tests/typ/image-jpeg.typ
new file mode 100644
index 00000000..48cf1a0d
--- /dev/null
+++ b/tests/typ/image-jpeg.typ
@@ -0,0 +1,2 @@
+// Load an RGB JPEG image.
+[image: "res/tiger.jpg"]
diff --git a/tests/typ/image-png.typ b/tests/typ/image-png.typ
new file mode 100644
index 00000000..482591e9
--- /dev/null
+++ b/tests/typ/image-png.typ
@@ -0,0 +1,2 @@
+// Load an RGBA PNG image.
+[image: "res/rhino.png"]
diff --git a/tests/typ/image.typ b/tests/typ/image.typ
deleted file mode 100644
index 6ae349a1..00000000
--- a/tests/typ/image.typ
+++ /dev/null
@@ -1,15 +0,0 @@
-[page: width=5cm, height=5cm, margins=0.25cm]
-
-[image: "res/tiger.jpg"]
-
-[pagebreak]
-
-# Tiger
-[image: "res/tiger.jpg", width=2cm]
-[image: "res/rhino.png", width=1cm]
-[image: "res/rhino.png", height=2cm]
-
-[pagebreak]
-
-[align: center, bottom]
-[image: "res/tiger.jpg", width=2cm, height=3.5cm]
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 807215ed..037bd7ef 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -1,35 +1,35 @@
use std::cell::RefCell;
use std::env;
use std::ffi::OsStr;
-use std::fs::{self, File};
+use std::fs;
use std::path::Path;
use std::rc::Rc;
use fontdock::fs::{FsIndex, FsSource};
use image::{GenericImageView, Rgba};
-use memmap::Mmap;
use tiny_skia::{
Canvas, Color, ColorU8, FillRule, FilterQuality, Paint, PathBuilder, Pattern, Pixmap,
Rect, SpreadMode, Transform,
};
use ttf_parser::OutlineBuilder;
-use typst::diag::{Feedback, Pass};
+use typst::diag::{Diag, Feedback, Level, Pass};
use typst::env::{Env, ImageResource, ResourceLoader, SharedEnv};
use typst::eval::State;
use typst::export::pdf;
use typst::font::FontLoader;
-use typst::geom::{Length, Point};
+use typst::geom::{Length, Point, Sides, Size};
use typst::layout::{BoxLayout, ImageElement, LayoutElement};
-use typst::parse::LineMap;
+use typst::parse::{LineMap, Scanner};
use typst::shaping::Shaped;
+use typst::syntax::{Location, Pos, SpanVec, SpanWith, Spanned};
use typst::typeset;
-const FONT_DIR: &str = "../fonts";
const TYP_DIR: &str = "typ";
-const PDF_DIR: &str = "pdf";
-const PNG_DIR: &str = "png";
-const CMP_DIR: &str = "cmp";
+const REF_DIR: &str = "ref";
+const PNG_DIR: &str = "out/png";
+const PDF_DIR: &str = "out/pdf";
+const FONT_DIR: &str = "../fonts";
fn main() {
env::set_current_dir(env::current_dir().unwrap().join("tests")).unwrap();
@@ -44,12 +44,8 @@ fn main() {
}
let name = src_path.file_stem().unwrap().to_string_lossy().to_string();
- let pdf_path = Path::new(PDF_DIR).join(&name).with_extension("pdf");
- let png_path = Path::new(PNG_DIR).join(&name).with_extension("png");
- let ref_path = Path::new(CMP_DIR).join(&name).with_extension("png");
-
if filter.matches(&name) {
- filtered.push((name, src_path, pdf_path, png_path, ref_path));
+ filtered.push((name, src_path));
}
}
@@ -62,8 +58,8 @@ fn main() {
println!("Running {} tests", len);
}
- fs::create_dir_all(PDF_DIR).unwrap();
fs::create_dir_all(PNG_DIR).unwrap();
+ fs::create_dir_all(PDF_DIR).unwrap();
let mut index = FsIndex::new();
index.search_dir(FONT_DIR);
@@ -76,29 +72,12 @@ fn main() {
let mut ok = true;
- for (name, src_path, pdf_path, png_path, ref_path) in filtered {
- print!("Testing {}.", name);
- test(&src_path, &pdf_path, &png_path, &env);
-
- let png_file = File::open(&png_path).unwrap();
- let ref_file = match File::open(&ref_path) {
- Ok(file) => file,
- Err(_) => {
- println!(" Failed to open reference image. ❌");
- ok = false;
- continue;
- }
- };
-
- let a = unsafe { Mmap::map(&png_file).unwrap() };
- let b = unsafe { Mmap::map(&ref_file).unwrap() };
+ for (name, src_path) in filtered {
+ let png_path = Path::new(PNG_DIR).join(&name).with_extension("png");
+ let pdf_path = Path::new(PDF_DIR).join(&name).with_extension("pdf");
+ let ref_path = Path::new(REF_DIR).join(&name).with_extension("png");
- if *a != *b {
- println!(" Does not match reference image. ❌");
- ok = false;
- } else {
- println!(" Okay. ✔");
- }
+ ok &= test(&name, &src_path, &pdf_path, &png_path, &ref_path, &env);
}
if !ok {
@@ -106,41 +85,6 @@ fn main() {
}
}
-fn test(src_path: &Path, pdf_path: &Path, png_path: &Path, env: &SharedEnv) {
- let src = fs::read_to_string(src_path).unwrap();
- let state = State::default();
- let Pass {
- output: layouts,
- feedback: Feedback { mut diags, .. },
- } = typeset(&src, Rc::clone(env), state);
-
- if !diags.is_empty() {
- diags.sort();
-
- let map = LineMap::new(&src);
- for diag in diags {
- let span = diag.span;
- let start = map.location(span.start);
- let end = map.location(span.end);
- println!(
- " {}: {}:{}-{}: {}",
- diag.v.level,
- src_path.display(),
- start,
- end,
- diag.v.message,
- );
- }
- }
-
- let env = env.borrow();
- let canvas = draw(&layouts, &env, 2.0);
- canvas.pixmap.save_png(png_path).unwrap();
-
- let pdf_data = pdf::export(&layouts, &env);
- fs::write(pdf_path, pdf_data).unwrap();
-}
-
struct TestFilter {
filter: Vec<String>,
perfect: bool,
@@ -171,6 +115,111 @@ impl TestFilter {
}
}
+fn test(
+ name: &str,
+ src_path: &Path,
+ pdf_path: &Path,
+ png_path: &Path,
+ ref_path: &Path,
+ env: &SharedEnv,
+) -> bool {
+ println!("Testing {}.", name);
+
+ let src = fs::read_to_string(src_path).unwrap();
+ let map = LineMap::new(&src);
+ let ref_diags = parse_diags(&src, &map);
+
+ let mut state = State::default();
+ state.page.size = Size::uniform(Length::pt(120.0));
+ state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
+
+ let Pass {
+ output: layouts,
+ feedback: Feedback { mut diags, .. },
+ } = typeset(&src, Rc::clone(env), state);
+ diags.sort();
+
+ let env = env.borrow();
+ let canvas = draw(&layouts, &env, 2.0);
+ canvas.pixmap.save_png(png_path).unwrap();
+
+ let pdf_data = pdf::export(&layouts, &env);
+ fs::write(pdf_path, pdf_data).unwrap();
+
+ let mut ok = true;
+
+ if diags != ref_diags {
+ println!(" Does not match expected diagnostics. ❌");
+ ok = false;
+
+ for diag in &diags {
+ if ref_diags.binary_search(diag).is_err() {
+ print!(" Unexpected | ");
+ print_diag(diag, &map);
+ }
+ }
+
+ for diag in &ref_diags {
+ if diags.binary_search(diag).is_err() {
+ print!(" Missing | ");
+ print_diag(diag, &map);
+ }
+ }
+ }
+
+ if let Ok(ref_pixmap) = Pixmap::load_png(&ref_path) {
+ if canvas.pixmap != ref_pixmap {
+ println!(" Does not match reference image. ❌");
+ ok = false;
+ }
+ } else {
+ println!(" Failed to open reference image. ❌");
+ ok = false;
+ }
+
+ if ok {
+ println!("\x1b[1ATesting {}. ✔", name);
+ }
+
+ ok
+}
+
+fn parse_diags(src: &str, map: &LineMap) -> SpanVec<Diag> {
+ let mut diags = vec![];
+
+ for line in src.lines() {
+ let (level, rest) = if let Some(rest) = line.strip_prefix("// error: ") {
+ (Level::Error, rest)
+ } else if let Some(rest) = line.strip_prefix("// warning: ") {
+ (Level::Warning, rest)
+ } else {
+ continue;
+ };
+
+ fn pos(s: &mut Scanner, map: &LineMap) -> Pos {
+ let (line, _, column) = (num(s), s.eat_assert(':'), num(s));
+ map.pos(Location { line, column }).unwrap()
+ }
+
+ fn num(s: &mut Scanner) -> u32 {
+ s.eat_while(|c| c.is_numeric()).parse().unwrap()
+ }
+
+ let mut s = Scanner::new(rest);
+ let (start, _, end) = (pos(&mut s, map), s.eat_assert('-'), pos(&mut s, map));
+ diags.push(Diag::new(level, s.rest().trim()).span_with(start .. end));
+ }
+
+ diags.sort();
+ diags
+}
+
+fn print_diag(diag: &Spanned<Diag>, map: &LineMap) {
+ let start = map.location(diag.span.start).unwrap();
+ let end = map.location(diag.span.end).unwrap();
+ println!("{}: {}-{}: {}", diag.v.level, start, end, diag.v.message,);
+}
+
fn draw(layouts: &[BoxLayout], env: &Env, pixel_per_pt: f32) -> Canvas {
let pad = Length::pt(5.0);