summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-02-11 19:26:47 +0100
committerLaurenz <laurmaedje@gmail.com>2021-02-11 19:26:47 +0100
commit146eda102a5d0241c96a808a847f3b855340765e (patch)
tree9fedbd9807d7db81d6da79738c5d41b910075251
parent1711b67877ce5c290e049775c340c9324f15341e (diff)
Move span directly into diagnostics 🚚
-rw-r--r--src/diag.rs116
-rw-r--r--src/eval/mod.rs22
-rw-r--r--src/exec/context.rs24
-rw-r--r--src/lib.rs8
-rw-r--r--src/main.rs19
-rw-r--r--src/parse/mod.rs2
-rw-r--r--src/parse/parser.rs27
-rw-r--r--src/prelude.rs2
-rw-r--r--src/pretty.rs7
-rw-r--r--tests/typeset.rs26
10 files changed, 98 insertions, 155 deletions
diff --git a/src/diag.rs b/src/diag.rs
index b1c477a5..d8433141 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -1,58 +1,64 @@
-//! Diagnostics and decorations for source code.
+//! Diagnostics for source code.
//!
//! Errors are never fatal, the document will always compile and yield a layout.
-use crate::syntax::Spanned;
+use std::collections::BTreeSet;
use std::fmt::{self, Display, Formatter};
-/// The result of some pass: Some output `T` and [`Feedback`] data.
+use crate::syntax::Span;
+
+/// The result of some pass: Some output `T` and diagnostics.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Pass<T> {
/// The output of this compilation pass.
pub output: T,
- /// User feedback data accumulated in this pass.
- pub feedback: Feedback,
+ /// User diagnostics accumulated in this pass.
+ pub diags: DiagSet,
}
impl<T> Pass<T> {
- /// Create a new pass from output and feedback data.
- pub fn new(output: T, feedback: Feedback) -> Self {
- Self { output, feedback }
+ /// Create a new pass from output and diagnostics.
+ pub fn new(output: T, diags: DiagSet) -> Self {
+ Self { output, diags }
}
}
-/// Diagnostics and semantic syntax highlighting information.
-#[derive(Debug, Default, Clone, Eq, PartialEq)]
-pub struct Feedback {
- /// Diagnostics about the source code.
- pub diags: Vec<Spanned<Diag>>,
- /// Decorations of the source code for semantic syntax highlighting.
- pub decos: Vec<Spanned<Deco>>,
-}
-
-impl Feedback {
- /// Create a new feedback instance without errors and decos.
- pub fn new() -> Self {
- Self { diags: vec![], decos: vec![] }
- }
-
- /// Add other feedback data to this feedback.
- pub fn extend(&mut self, more: Self) {
- self.diags.extend(more.diags);
- self.decos.extend(more.decos);
- }
-}
+/// A set of diagnostics.
+///
+/// Since this is a [`BTreeSet`], there cannot be two equal (up to span)
+/// diagnostics and you can quickly iterate diagnostics in source location
+/// order.
+pub type DiagSet = BTreeSet<Diag>;
/// A diagnostic with severity level and message.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Diag {
+ /// The source code location.
+ pub span: Span,
/// How severe / important the diagnostic is.
pub level: Level,
/// A message describing the diagnostic.
pub message: String,
}
+impl Diag {
+ /// Create a new diagnostic from message and level.
+ pub fn new(span: impl Into<Span>, level: Level, message: impl Into<String>) -> Self {
+ Self {
+ span: span.into(),
+ level,
+ message: message.into(),
+ }
+ }
+}
+
+impl Display for Diag {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}: {}", self.level, self.message)
+ }
+}
+
/// How severe / important a diagnostic is.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
@@ -62,13 +68,6 @@ pub enum Level {
Error,
}
-impl Diag {
- /// Create a new diagnostic from message and level.
- pub fn new(level: Level, message: impl Into<String>) -> Self {
- Self { level, message: message.into() }
- }
-}
-
impl Display for Level {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
@@ -78,17 +77,6 @@ impl Display for Level {
}
}
-/// Decorations for semantic syntax highlighting.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
-pub enum Deco {
- /// Strong text.
- Strong,
- /// Emphasized text.
- Emph,
-}
-
/// Construct a diagnostic with [`Error`](Level::Error) level.
///
/// ```
@@ -96,16 +84,16 @@ pub enum Deco {
/// # use typst::syntax::Span;
/// # let span = Span::ZERO;
/// # let name = "";
-/// // Create formatted error values.
-/// let error = error!("expected {}", name);
-///
-/// // Create spanned errors.
-/// let spanned = error!(span, "there is an error here");
+/// let error = error!(span, "there is an error with {}", name);
/// ```
#[macro_export]
macro_rules! error {
- ($($tts:tt)*) => {
- $crate::__impl_diagnostic!($crate::diag::Level::Error; $($tts)*)
+ ($span:expr, $($tts:tt)*) => {
+ $crate::diag::Diag::new(
+ $span,
+ $crate::diag::Level::Error,
+ format!($($tts)*),
+ )
};
}
@@ -115,23 +103,11 @@ macro_rules! error {
/// information.
#[macro_export]
macro_rules! warning {
- ($($tts:tt)*) => {
- $crate::__impl_diagnostic!($crate::diag::Level::Warning; $($tts)*)
- };
-}
-
-/// Backs the `error!` and `warning!` macros.
-#[macro_export]
-#[doc(hidden)]
-macro_rules! __impl_diagnostic {
- ($level:expr; $fmt:literal $($tts:tt)*) => {
- $crate::diag::Diag::new($level, format!($fmt $($tts)*))
- };
-
- ($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => {
- $crate::syntax::Spanned::new(
- $crate::__impl_diagnostic!($level; $fmt $($tts)*),
+ ($span:expr, $($tts:tt)*) => {
+ $crate::diag::Diag::new(
$span,
+ $crate::diag::Level::Warning,
+ format!($($tts)*),
)
};
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index ec71f689..57a37f38 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -15,7 +15,7 @@ use std::rc::Rc;
use super::*;
use crate::color::Color;
-use crate::diag::{Diag, Feedback};
+use crate::diag::{Diag, DiagSet};
use crate::geom::{Angle, Length, Relative};
use crate::syntax::visit::Visit;
use crate::syntax::*;
@@ -27,14 +27,14 @@ use crate::syntax::*;
pub fn eval(env: &mut Env, tree: &Tree, scope: &Scope) -> Pass<ExprMap> {
let mut ctx = EvalContext::new(env, scope);
let map = tree.eval(&mut ctx);
- Pass::new(map, ctx.feedback)
+ Pass::new(map, ctx.diags)
}
/// A map from expression to values to evaluated to.
///
-/// The raw pointers point into the expressions contained in `tree`. Since
-/// the lifetime is erased, `tree` could go out of scope while the hash map
-/// still lives. While this could lead to lookup panics, it is not unsafe
+/// The raw pointers point into the expressions contained in some [tree](Tree).
+/// Since the lifetime is erased, the tree could go out of scope while the hash
+/// map still lives. Though this could lead to lookup panics, it is not unsafe
/// since the pointers are never dereferenced.
pub type ExprMap = HashMap<*const Expr, Value>;
@@ -45,8 +45,8 @@ pub struct EvalContext<'a> {
pub env: &'a mut Env,
/// The active scopes.
pub scopes: Scopes<'a>,
- /// The accumulated feedback.
- feedback: Feedback,
+ /// Evaluation diagnostics.
+ pub diags: DiagSet,
}
impl<'a> EvalContext<'a> {
@@ -55,13 +55,13 @@ impl<'a> EvalContext<'a> {
Self {
env,
scopes: Scopes::with_base(scope),
- feedback: Feedback::new(),
+ diags: DiagSet::new(),
}
}
- /// Add a diagnostic to the feedback.
- pub fn diag(&mut self, diag: Spanned<Diag>) {
- self.feedback.diags.push(diag);
+ /// Add a diagnostic.
+ pub fn diag(&mut self, diag: Diag) {
+ self.diags.insert(diag);
}
}
diff --git a/src/exec/context.rs b/src/exec/context.rs
index a1998f29..821a2616 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -4,8 +4,7 @@ use std::rc::Rc;
use fontdock::FontStyle;
use super::*;
-use crate::diag::Diag;
-use crate::diag::{Deco, Feedback, Pass};
+use crate::diag::{Diag, DiagSet};
use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
use crate::layout::{
Expansion, Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
@@ -18,8 +17,8 @@ pub struct ExecContext<'a> {
pub env: &'a mut Env,
/// The active execution state.
pub state: State,
- /// The accumulated feedback.
- feedback: Feedback,
+ /// Execution diagnostics.
+ pub diags: DiagSet,
/// The finished page runs.
runs: Vec<NodePages>,
/// The stack of logical groups (paragraphs and such).
@@ -39,27 +38,22 @@ impl<'a> ExecContext<'a> {
Self {
env,
state,
+ diags: DiagSet::new(),
+ runs: vec![],
groups: vec![],
inner: vec![],
- runs: vec![],
- feedback: Feedback::new(),
}
}
/// Finish execution and return the created layout tree.
pub fn finish(self) -> Pass<Tree> {
assert!(self.groups.is_empty(), "unfinished group");
- Pass::new(Tree { runs: self.runs }, self.feedback)
- }
-
- /// Add a diagnostic to the feedback.
- pub fn diag(&mut self, diag: Spanned<Diag>) {
- self.feedback.diags.push(diag);
+ Pass::new(Tree { runs: self.runs }, self.diags)
}
- /// Add a decoration to the feedback.
- pub fn deco(&mut self, deco: Spanned<Deco>) {
- self.feedback.decos.push(deco);
+ /// Add a diagnostic.
+ pub fn diag(&mut self, diag: Diag) {
+ self.diags.insert(diag);
}
/// Push a layout node to the active group.
diff --git a/src/lib.rs b/src/lib.rs
index efdbb4cb..c8d41317 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -65,9 +65,9 @@ pub fn typeset(
let executed = exec::exec(env, &parsed.output, &evaluated.output, state);
let frames = layout::layout(env, &executed.output);
- let mut feedback = parsed.feedback;
- feedback.extend(evaluated.feedback);
- feedback.extend(executed.feedback);
+ let mut diags = parsed.diags;
+ diags.extend(evaluated.diags);
+ diags.extend(executed.diags);
- Pass::new(frames, feedback)
+ Pass::new(frames, diags)
}
diff --git a/src/main.rs b/src/main.rs
index d9a4f096..c221f3b6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use anyhow::{anyhow, bail, Context};
use fontdock::fs::FsIndex;
-use typst::diag::{Feedback, Pass};
+use typst::diag::Pass;
use typst::env::{Env, ResourceLoader};
use typst::exec::State;
use typst::export::pdf;
@@ -48,26 +48,19 @@ fn main() -> anyhow::Result<()> {
let scope = library::new();
let state = State::default();
- let Pass {
- output: frames,
- feedback: Feedback { mut diags, .. },
- } = typeset(&mut env, &src, &scope, state);
-
+ let Pass { output: frames, diags } = typeset(&mut env, &src, &scope, 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).unwrap();
- let end = map.location(span.end).unwrap();
+ let start = map.location(diag.span.start).unwrap();
+ let end = map.location(diag.span.end).unwrap();
println!(
"{}: {}:{}-{}: {}",
- diag.v.level,
+ diag.level,
src_path.display(),
start,
end,
- diag.v.message,
+ diag.message,
);
}
}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index e7e25a1d..8780efaf 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -22,7 +22,7 @@ use collection::{args, parenthesized};
/// Parse a string of source code.
pub fn parse(src: &str) -> Pass<Tree> {
let mut p = Parser::new(src);
- Pass::new(tree(&mut p), p.finish())
+ Pass::new(tree(&mut p), p.diags)
}
/// Parse a syntax tree.
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index a64e39dd..7c660182 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -1,12 +1,13 @@
use std::fmt::{self, Debug, Formatter};
use super::{Scanner, TokenMode, Tokens};
-use crate::diag::Diag;
-use crate::diag::{Deco, Feedback};
-use crate::syntax::{Pos, Span, Spanned, Token};
+use crate::diag::{Diag, DiagSet};
+use crate::syntax::{Pos, Span, Token};
/// A convenient token-based parser.
pub struct Parser<'s> {
+ /// Parsing diagnostics.
+ pub diags: DiagSet,
/// An iterator over the source tokens.
tokens: Tokens<'s>,
/// The next token.
@@ -20,8 +21,6 @@ pub struct Parser<'s> {
last_end: Pos,
/// The stack of open groups.
groups: Vec<GroupEntry>,
- /// Accumulated feedback.
- feedback: Feedback,
}
/// A logical group of tokens, e.g. `[...]`.
@@ -67,18 +66,13 @@ impl<'s> Parser<'s> {
next_start: Pos::ZERO,
last_end: Pos::ZERO,
groups: vec![],
- feedback: Feedback::new(),
+ diags: DiagSet::new(),
}
}
- /// Finish parsing and return the accumulated feedback.
- pub fn finish(self) -> Feedback {
- self.feedback
- }
-
- /// Add a diagnostic to the feedback.
- pub fn diag(&mut self, diag: Spanned<Diag>) {
- self.feedback.diags.push(diag);
+ /// Add a diagnostic.
+ pub fn diag(&mut self, diag: Diag) {
+ self.diags.insert(diag);
}
/// Eat the next token and add a diagnostic that it is not the expected
@@ -112,11 +106,6 @@ impl<'s> Parser<'s> {
}
}
- /// Add a decoration to the feedback.
- pub fn deco(&mut self, deco: Spanned<Deco>) {
- self.feedback.decos.push(deco);
- }
-
/// Continue parsing in a group.
///
/// When the end delimiter of the group is reached, all subsequent calls to
diff --git a/src/prelude.rs b/src/prelude.rs
index 5beded39..1251bd29 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -1,6 +1,6 @@
//! A prelude for building custom functions.
-pub use crate::diag::{Feedback, Pass};
+pub use crate::diag::{Diag, Pass};
#[doc(no_inline)]
pub use crate::eval::{
CastResult, Eval, EvalContext, TemplateAny, TemplateNode, Value, ValueAny, ValueArgs,
diff --git a/src/pretty.rs b/src/pretty.rs
index a522b125..025bc66c 100644
--- a/src/pretty.rs
+++ b/src/pretty.rs
@@ -1,6 +1,6 @@
//! Pretty printing.
-use std::fmt::{Arguments, Result, Write};
+use std::fmt::{self, Arguments, Write};
use crate::color::{Color, RgbaColor};
use crate::eval::*;
@@ -70,7 +70,7 @@ impl Printer {
}
/// Write formatted items into the buffer.
- pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result {
+ pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> fmt::Result {
Write::write_fmt(self, fmt)
}
@@ -97,7 +97,7 @@ impl Printer {
}
impl Write for Printer {
- fn write_str(&mut self, s: &str) -> Result {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
@@ -484,6 +484,7 @@ impl Pretty for Value {
Value::Relative(v) => v.pretty(p),
Value::Linear(v) => v.pretty(p),
Value::Color(v) => v.pretty(p),
+ // TODO: Handle like text when directly in template.
Value::Str(v) => v.pretty(p),
Value::Array(v) => v.pretty(p),
Value::Dict(v) => v.pretty(p),
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 4b459a93..8932c483 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -14,7 +14,7 @@ use tiny_skia::{
use ttf_parser::OutlineBuilder;
use walkdir::WalkDir;
-use typst::diag::{Diag, Feedback, Level, Pass};
+use typst::diag::{Diag, DiagSet, Level, Pass};
use typst::env::{Env, ImageResource, ResourceLoader};
use typst::eval::{EvalContext, Scope, Value, ValueArgs, ValueFunc};
use typst::exec::State;
@@ -25,7 +25,7 @@ use typst::layout::{Element, Expansion, Fill, Frame, Geometry, Image, Shape};
use typst::library;
use typst::parse::{LineMap, Scanner};
use typst::shaping::Shaped;
-use typst::syntax::{Location, Pos, Spanned};
+use typst::syntax::{Location, Pos};
use typst::typeset;
const TYP_DIR: &str = "typ";
@@ -219,17 +219,11 @@ fn test_part(
state.page.expand = Spec::new(Expansion::Fill, Expansion::Fit);
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
- let Pass {
- output: mut frames,
- feedback: Feedback { mut diags, .. },
- } = typeset(env, &src, &scope, state);
-
+ let Pass { output: mut frames, diags } = typeset(env, &src, &scope, state);
if !compare_ref {
frames.clear();
}
- diags.sort_by_key(|d| d.span);
-
let mut ok = true;
for panic in &*panics.borrow() {
@@ -266,8 +260,8 @@ fn test_part(
(ok, frames)
}
-fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, Vec<Spanned<Diag>>) {
- let mut diags = vec![];
+fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, DiagSet) {
+ let mut diags = DiagSet::new();
let mut compare_ref = None;
for (i, line) in src.lines().enumerate() {
@@ -303,13 +297,9 @@ fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, Vec<Spanned<Diag>>
let mut s = Scanner::new(rest);
let (start, _, end) = (pos(&mut s), s.eat_assert('-'), pos(&mut s));
-
- let diag = Diag::new(level, s.rest().trim());
- diags.push(Spanned::new(diag, start .. end));
+ diags.insert(Diag::new(start .. end, level, s.rest().trim()));
}
- diags.sort_by_key(|d| d.span);
-
(compare_ref, diags)
}
@@ -341,12 +331,12 @@ fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
scope.def_const("test", ValueFunc::new("test", test));
}
-fn print_diag(diag: &Spanned<Diag>, map: &LineMap, lines: u32) {
+fn print_diag(diag: &Diag, map: &LineMap, lines: u32) {
let mut start = map.location(diag.span.start).unwrap();
let mut end = map.location(diag.span.end).unwrap();
start.line += lines;
end.line += lines;
- println!("{}: {}-{}: {}", diag.v.level, start, end, diag.v.message);
+ println!("{}: {}-{}: {}", diag.level, start, end, diag.message);
}
fn draw(frames: &[Frame], env: &Env, pixel_per_pt: f32) -> Canvas {