Enable multiple tests per file 🧱
13
.gitignore
vendored
@ -1,8 +1,15 @@
|
|||||||
.vscode
|
.vscode
|
||||||
|
_things
|
||||||
|
|
||||||
/target
|
/target
|
||||||
|
bench/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
bench/target
|
|
||||||
tests/out
|
tests/png
|
||||||
_things
|
tests/pdf
|
||||||
|
tests/playground.typ
|
||||||
|
tests/playground.png
|
||||||
|
tests/playground.pdf
|
||||||
|
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
`oxipng -o max tests/ref/<img>` when creating or updating reference
|
`oxipng -o max tests/ref/<img>` when creating or updating reference
|
||||||
images (note that `<img>` can be `*` to optimize all images).
|
images (note that `<img>` can be `*` to optimize all images).
|
||||||
- `res`: Resource files used by tests.
|
- `res`: Resource files used by tests.
|
||||||
- `out`: PNG and PDF files produced by tests.
|
- `png`: PNG files produced by tests.
|
||||||
|
- `pdf`: PDF files produced by tests.
|
||||||
|
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.2 KiB |
BIN
tests/ref/func-font.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 186 KiB |
Before Width: | Height: | Size: 105 KiB |
BIN
tests/ref/func-image.png
Normal file
After Width: | Height: | Size: 295 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 5.0 KiB |
BIN
tests/ref/func-page.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
@ -1,21 +0,0 @@
|
|||||||
// Test error cases of the `font` function.
|
|
||||||
|
|
||||||
// Not one of the valid things for positional arguments.
|
|
||||||
[font false]
|
|
||||||
|
|
||||||
// Wrong types.
|
|
||||||
[font style: bold, weight: "thin", serif: 0]
|
|
||||||
|
|
||||||
// Weight out of range.
|
|
||||||
[font weight: 2700]
|
|
||||||
|
|
||||||
// Non-existing argument.
|
|
||||||
[font something: "invalid"]
|
|
||||||
|
|
||||||
// compare-ref: false
|
|
||||||
// error: 4:7-4:12 unexpected argument
|
|
||||||
// error: 7:14-7:18 expected font style, found font weight
|
|
||||||
// error: 7:28-7:34 expected font weight, found string
|
|
||||||
// error: 7:43-7:44 expected font family or array of font families, found integer
|
|
||||||
// warning: 10:15-10:19 must be between 100 and 900
|
|
||||||
// error: 13:7-13:27 unexpected argument
|
|
@ -1,20 +0,0 @@
|
|||||||
// Test font fallback.
|
|
||||||
|
|
||||||
// Source Sans Pro + Segoe UI Emoji.
|
|
||||||
Emoji: 🏀
|
|
||||||
|
|
||||||
// CMU Serif + Noto Emoji.
|
|
||||||
[font "CMU Serif", "Noto Emoji"][
|
|
||||||
Emoji: 🏀
|
|
||||||
]
|
|
||||||
|
|
||||||
// Class definitions.
|
|
||||||
[font serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji")]
|
|
||||||
[font serif][
|
|
||||||
Math: ∫ α + β ➗ 3
|
|
||||||
]
|
|
||||||
|
|
||||||
// Class definition reused.
|
|
||||||
[font sans-serif: "Noto Emoji"]
|
|
||||||
[font sans-serif: ("Archivo", sans-serif)]
|
|
||||||
New sans-serif. 🚀
|
|
@ -1,20 +0,0 @@
|
|||||||
// Test configuring font properties.
|
|
||||||
|
|
||||||
[font "PT Sans", 10pt]
|
|
||||||
|
|
||||||
// Set same font size in three different ways.
|
|
||||||
[font 20pt][A]
|
|
||||||
[font 200%][A]
|
|
||||||
[font 15pt + 50%][A]
|
|
||||||
|
|
||||||
// Do nothing.
|
|
||||||
[font][Normal]
|
|
||||||
|
|
||||||
// Set style (is available).
|
|
||||||
[font style: italic][Italic]
|
|
||||||
|
|
||||||
// Set weight (is available).
|
|
||||||
[font weight: bold][Bold]
|
|
||||||
|
|
||||||
// Set stretch (not available, matching closest).
|
|
||||||
[font stretch: ultra-condensed][Condensed]
|
|
65
tests/typ/func-font.typ
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Test configuring font properties.
|
||||||
|
|
||||||
|
[font "PT Sans", 10pt]
|
||||||
|
|
||||||
|
// Set same font size in three different ways.
|
||||||
|
[font 20pt][A]
|
||||||
|
[font 200%][A]
|
||||||
|
[font 15pt + 50%][A]
|
||||||
|
|
||||||
|
// Do nothing.
|
||||||
|
[font][Normal]
|
||||||
|
|
||||||
|
// Set style (is available).
|
||||||
|
[font style: italic][Italic]
|
||||||
|
|
||||||
|
// Set weight (is available).
|
||||||
|
[font weight: bold][Bold]
|
||||||
|
|
||||||
|
// Set stretch (not available, matching closest).
|
||||||
|
[font stretch: ultra-condensed][Condensed]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test font fallback.
|
||||||
|
|
||||||
|
// Source Sans Pro + Segoe UI Emoji.
|
||||||
|
Emoji: 🏀
|
||||||
|
|
||||||
|
// CMU Serif + Noto Emoji.
|
||||||
|
[font "CMU Serif", "Noto Emoji"][
|
||||||
|
Emoji: 🏀
|
||||||
|
]
|
||||||
|
|
||||||
|
// Class definitions.
|
||||||
|
[font serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji")]
|
||||||
|
[font serif][
|
||||||
|
Math: ∫ α + β ➗ 3
|
||||||
|
]
|
||||||
|
|
||||||
|
// Class definition reused.
|
||||||
|
[font sans-serif: "Noto Emoji"]
|
||||||
|
[font sans-serif: ("Archivo", sans-serif)]
|
||||||
|
New sans-serif. 🚀
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test error cases.
|
||||||
|
//
|
||||||
|
// ref: false
|
||||||
|
// error: 3:7-3:12 unexpected argument
|
||||||
|
// error: 6:14-6:18 expected font style, found font weight
|
||||||
|
// error: 6:28-6:34 expected font weight, found string
|
||||||
|
// error: 6:43-6:44 expected font family or array of font families, found integer
|
||||||
|
// warning: 9:15-9:19 must be between 100 and 900
|
||||||
|
// error: 12:7-12:27 unexpected argument
|
||||||
|
|
||||||
|
// Not one of the valid things for positional arguments.
|
||||||
|
[font false]
|
||||||
|
|
||||||
|
// Wrong types.
|
||||||
|
[font style: bold, weight: "thin", serif: 0]
|
||||||
|
|
||||||
|
// Weight out of range.
|
||||||
|
[font weight: 2700]
|
||||||
|
|
||||||
|
// Non-existing argument.
|
||||||
|
[font something: "invalid"]
|
@ -12,9 +12,6 @@ Add [h 10pt] [h 10pt] up
|
|||||||
// Relative to font size.
|
// Relative to font size.
|
||||||
Relative [h 100%] spacing
|
Relative [h 100%] spacing
|
||||||
|
|
||||||
// Missing spacing.
|
|
||||||
Totally [h] ignored
|
|
||||||
|
|
||||||
// Swapped axes.
|
// Swapped axes.
|
||||||
[page main-dir: rtl, cross-dir: ttb][
|
[page main-dir: rtl, cross-dir: ttb][
|
||||||
1 [h 1cm] 2
|
1 [h 1cm] 2
|
||||||
@ -22,4 +19,10 @@ Totally [h] ignored
|
|||||||
3 [v 1cm] 4 [v -1cm] 5
|
3 [v 1cm] 4 [v -1cm] 5
|
||||||
]
|
]
|
||||||
|
|
||||||
// error: 16:11-16:11 missing argument: spacing
|
---
|
||||||
|
// Test error cases.
|
||||||
|
//
|
||||||
|
// error: 3:11-3:11 missing argument: spacing
|
||||||
|
|
||||||
|
// Missing spacing.
|
||||||
|
Totally [h] ignored
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
// Test error cases of the `image` function.
|
|
||||||
|
|
||||||
// File does not exist.
|
|
||||||
[image "path/does/not/exist"]
|
|
||||||
|
|
||||||
// File exists, but is no image.
|
|
||||||
[image "typ/image-error.typ"]
|
|
||||||
|
|
||||||
// compare-ref: false
|
|
||||||
// error: 4:8-4:29 failed to load image
|
|
||||||
// error: 7:8-7:29 failed to load image
|
|
@ -1,8 +0,0 @@
|
|||||||
// Test loading different image formats.
|
|
||||||
|
|
||||||
// Load an RGBA PNG image.
|
|
||||||
[image "res/rhino.png"]
|
|
||||||
[pagebreak]
|
|
||||||
|
|
||||||
// Load an RGB JPEG image.
|
|
||||||
[image "res/tiger.jpg"]
|
|
@ -1,3 +1,13 @@
|
|||||||
|
// Test loading different image formats.
|
||||||
|
|
||||||
|
// Load an RGBA PNG image.
|
||||||
|
[image "res/rhino.png"]
|
||||||
|
[pagebreak]
|
||||||
|
|
||||||
|
// Load an RGB JPEG image.
|
||||||
|
[image "res/tiger.jpg"]
|
||||||
|
|
||||||
|
---
|
||||||
// Test configuring the size and fitting behaviour of images.
|
// Test configuring the size and fitting behaviour of images.
|
||||||
|
|
||||||
// Fit to width of page.
|
// Fit to width of page.
|
||||||
@ -21,3 +31,16 @@
|
|||||||
[align bottom, right][
|
[align bottom, right][
|
||||||
[image "res/tiger.jpg"]
|
[image "res/tiger.jpg"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test error cases.
|
||||||
|
//
|
||||||
|
// ref: false
|
||||||
|
// error: 3:8-3:29 failed to load image
|
||||||
|
// error: 6:8-6:29 failed to load image
|
||||||
|
|
||||||
|
// File does not exist.
|
||||||
|
[image "path/does/not/exist"]
|
||||||
|
|
||||||
|
// File exists, but is no image.
|
||||||
|
[image "typ/image-error.typ"]
|
@ -1,11 +0,0 @@
|
|||||||
// Test a combination of pages with bodies and normal content.
|
|
||||||
|
|
||||||
[page height: 50pt]
|
|
||||||
|
|
||||||
[page][First]
|
|
||||||
[page][Second]
|
|
||||||
[pagebreak]
|
|
||||||
Fourth
|
|
||||||
[page][]
|
|
||||||
Sixth
|
|
||||||
[page][Seventh and last]
|
|
@ -1,5 +0,0 @@
|
|||||||
// Test changing the layouting directions of pages.
|
|
||||||
|
|
||||||
[page main-dir: btt, cross-dir: rtl]
|
|
||||||
|
|
||||||
Right to left!
|
|
@ -1,11 +0,0 @@
|
|||||||
// Test error cases of the `page` function.
|
|
||||||
|
|
||||||
// Invalid paper.
|
|
||||||
[page nonexistant]
|
|
||||||
|
|
||||||
// Aligned axes.
|
|
||||||
[page main-dir: ltr]
|
|
||||||
|
|
||||||
// compare-ref: false
|
|
||||||
// error: 4:7-4:18 unknown variable
|
|
||||||
// error: 7:17-7:20 aligned axis
|
|
@ -23,3 +23,37 @@
|
|||||||
|
|
||||||
// Flip the page.
|
// Flip the page.
|
||||||
[page "a10", flip: true][Flipped]
|
[page "a10", flip: true][Flipped]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test a combination of pages with bodies and normal content.
|
||||||
|
|
||||||
|
[page height: 50pt]
|
||||||
|
|
||||||
|
[page][First]
|
||||||
|
[page][Second]
|
||||||
|
[pagebreak]
|
||||||
|
Fourth
|
||||||
|
[page][]
|
||||||
|
Sixth
|
||||||
|
[page][Seventh and last]
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test changing the layouting directions of pages.
|
||||||
|
|
||||||
|
[page main-dir: btt, cross-dir: rtl]
|
||||||
|
|
||||||
|
Right to left!
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test error cases.
|
||||||
|
//
|
||||||
|
// ref: false
|
||||||
|
// error: 3:7-3:18 unknown variable
|
||||||
|
// error: 6:17-6:20 aligned axis
|
||||||
|
|
||||||
|
// Invalid paper.
|
||||||
|
[page nonexistant]
|
||||||
|
|
||||||
|
// Aligned axes.
|
||||||
|
[page main-dir: ltr]
|
@ -1,4 +1,11 @@
|
|||||||
// Test the `rgb` function.
|
// Test the `rgb` function.
|
||||||
|
//
|
||||||
|
// warning: 9:6-9:9 must be between 0.0 and 1.0
|
||||||
|
// warning: 9:11-9:15 must be between 0.0 and 1.0
|
||||||
|
// error: 12:6-12:10 missing argument: blue component
|
||||||
|
// error: 15:5-15:5 missing argument: red component
|
||||||
|
// error: 15:5-15:5 missing argument: green component
|
||||||
|
// error: 15:5-15:5 missing argument: blue component
|
||||||
|
|
||||||
// Check the output.
|
// Check the output.
|
||||||
[rgb 0.0, 0.3, 0.7]
|
[rgb 0.0, 0.3, 0.7]
|
||||||
@ -14,10 +21,3 @@
|
|||||||
|
|
||||||
// Missing all components.
|
// Missing all components.
|
||||||
[rgb]
|
[rgb]
|
||||||
|
|
||||||
// warning: 10:6-10:9 must be between 0.0 and 1.0
|
|
||||||
// warning: 10:11-10:15 must be between 0.0 and 1.0
|
|
||||||
// error: 13:6-13:10 missing argument: blue component
|
|
||||||
// error: 16:5-16:5 missing argument: red component
|
|
||||||
// error: 16:5-16:5 missing argument: green component
|
|
||||||
// error: 16:5-16:5 missing argument: blue component
|
|
||||||
|
163
tests/typeset.rs
@ -27,8 +27,8 @@ use typst::typeset;
|
|||||||
|
|
||||||
const TYP_DIR: &str = "typ";
|
const TYP_DIR: &str = "typ";
|
||||||
const REF_DIR: &str = "ref";
|
const REF_DIR: &str = "ref";
|
||||||
const PNG_DIR: &str = "out/png";
|
const PNG_DIR: &str = "png";
|
||||||
const PDF_DIR: &str = "out/pdf";
|
const PDF_DIR: &str = "pdf";
|
||||||
const FONT_DIR: &str = "../fonts";
|
const FONT_DIR: &str = "../fonts";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -76,8 +76,26 @@ fn main() {
|
|||||||
let png_path = Path::new(PNG_DIR).join(&name).with_extension("png");
|
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 pdf_path = Path::new(PDF_DIR).join(&name).with_extension("pdf");
|
||||||
let ref_path = Path::new(REF_DIR).join(&name).with_extension("png");
|
let ref_path = Path::new(REF_DIR).join(&name).with_extension("png");
|
||||||
|
ok &= test(
|
||||||
|
&name,
|
||||||
|
&src_path,
|
||||||
|
&png_path,
|
||||||
|
&pdf_path,
|
||||||
|
Some(&ref_path),
|
||||||
|
&env,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ok &= test(&name, &src_path, &pdf_path, &png_path, &ref_path, &env);
|
let playground = Path::new("playground.typ");
|
||||||
|
if playground.exists() {
|
||||||
|
test(
|
||||||
|
"playground",
|
||||||
|
playground,
|
||||||
|
Path::new("playground.png"),
|
||||||
|
Path::new("playground.pdf"),
|
||||||
|
None,
|
||||||
|
&env,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -118,38 +136,73 @@ impl TestFilter {
|
|||||||
fn test(
|
fn test(
|
||||||
name: &str,
|
name: &str,
|
||||||
src_path: &Path,
|
src_path: &Path,
|
||||||
pdf_path: &Path,
|
|
||||||
png_path: &Path,
|
png_path: &Path,
|
||||||
ref_path: &Path,
|
pdf_path: &Path,
|
||||||
|
ref_path: Option<&Path>,
|
||||||
env: &SharedEnv,
|
env: &SharedEnv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
println!("Testing {}.", name);
|
println!("Testing {}.", name);
|
||||||
|
|
||||||
let src = fs::read_to_string(src_path).unwrap();
|
let src = fs::read_to_string(src_path).unwrap();
|
||||||
let map = LineMap::new(&src);
|
|
||||||
let (ref_diags, compare_ref) = parse_metadata(&src, &map);
|
let mut ok = true;
|
||||||
|
let mut frames = vec![];
|
||||||
|
|
||||||
|
for (i, part) in src.split("---").enumerate() {
|
||||||
|
let (part_ok, part_frames) = test_part(i, part, env);
|
||||||
|
ok &= part_ok;
|
||||||
|
frames.extend(part_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
let env = env.borrow();
|
||||||
|
if !frames.is_empty() {
|
||||||
|
let canvas = draw(&frames, &env, 2.0);
|
||||||
|
canvas.pixmap.save_png(png_path).unwrap();
|
||||||
|
|
||||||
|
let pdf_data = pdf::export(&frames, &env);
|
||||||
|
fs::write(pdf_path, pdf_data).unwrap();
|
||||||
|
|
||||||
|
if let Some(ref_path) = ref_path {
|
||||||
|
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 test_part(i: usize, src: &str, env: &SharedEnv) -> (bool, Vec<Frame>) {
|
||||||
|
let (src, compare_ref, map, ref_diags) = parse_metadata(&src, i);
|
||||||
|
|
||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
state.page.size = Size::uniform(Length::pt(120.0));
|
state.page.size = Size::uniform(Length::pt(120.0));
|
||||||
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
|
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
|
||||||
|
|
||||||
let Pass {
|
let Pass {
|
||||||
output: frames,
|
output: mut frames,
|
||||||
feedback: Feedback { mut diags, .. },
|
feedback: Feedback { mut diags, .. },
|
||||||
} = typeset(&src, Rc::clone(env), state);
|
} = typeset(&src, Rc::clone(env), state);
|
||||||
|
|
||||||
|
if !compare_ref {
|
||||||
|
frames.clear();
|
||||||
|
}
|
||||||
|
|
||||||
diags.sort_by_key(|d| d.span);
|
diags.sort_by_key(|d| d.span);
|
||||||
|
|
||||||
let env = env.borrow();
|
|
||||||
let canvas = draw(&frames, &env, 2.0);
|
|
||||||
canvas.pixmap.save_png(png_path).unwrap();
|
|
||||||
|
|
||||||
let pdf_data = pdf::export(&frames, &env);
|
|
||||||
fs::write(pdf_path, pdf_data).unwrap();
|
|
||||||
|
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
|
|
||||||
if diags != ref_diags {
|
if diags != ref_diags {
|
||||||
println!(" Does not match expected diagnostics. ❌");
|
println!(" Subtest {} does not match expected diagnostics. ❌", i);
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
||||||
for diag in &diags {
|
for diag in &diags {
|
||||||
@ -167,57 +220,61 @@ fn test(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if compare_ref {
|
(ok, frames)
|
||||||
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_metadata(src: &str, map: &LineMap) -> (SpanVec<Diag>, bool) {
|
fn parse_metadata(src: &str, i: usize) -> (&str, bool, LineMap, SpanVec<Diag>) {
|
||||||
let mut diags = vec![];
|
let mut diags = vec![];
|
||||||
let mut compare_ref = true;
|
let mut compare_ref = true;
|
||||||
|
|
||||||
for line in src.lines() {
|
let mut s = Scanner::new(src);
|
||||||
compare_ref &= !line.starts_with("// compare-ref: false");
|
for k in 0 .. {
|
||||||
|
// Allow a newline directly after "---" (that is, if i > 0 and k == 0).
|
||||||
|
if !(i > 0 && k == 0) && !s.rest().starts_with("//") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let (level, rest) = if let Some(rest) = line.strip_prefix("// error: ") {
|
let line = s.eat_until(typst::parse::is_newline);
|
||||||
(Level::Error, rest)
|
s.eat_merging_crlf();
|
||||||
} else if let Some(rest) = line.strip_prefix("// warning: ") {
|
|
||||||
|
compare_ref &= !line.starts_with("// ref: false");
|
||||||
|
|
||||||
|
let (level, rest) = if let Some(rest) = line.strip_prefix("// warning: ") {
|
||||||
(Level::Warning, rest)
|
(Level::Warning, rest)
|
||||||
|
} else if let Some(rest) = line.strip_prefix("// error: ") {
|
||||||
|
(Level::Error, rest)
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn pos(s: &mut Scanner, map: &LineMap) -> Pos {
|
diags.push((level, rest));
|
||||||
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()).with_span(start .. end));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let src = s.rest();
|
||||||
|
let map = LineMap::new(src);
|
||||||
|
|
||||||
|
let mut diags: Vec<_> = diags
|
||||||
|
.into_iter()
|
||||||
|
.map(|(level, rest)| {
|
||||||
|
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));
|
||||||
|
Diag::new(level, s.rest().trim()).with_span(start .. end)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
diags.sort_by_key(|d| d.span);
|
diags.sort_by_key(|d| d.span);
|
||||||
|
|
||||||
(diags, compare_ref)
|
(src, compare_ref, map, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_diag(diag: &Spanned<Diag>, map: &LineMap) {
|
fn print_diag(diag: &Spanned<Diag>, map: &LineMap) {
|
||||||
|