mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Numbered spans
This commit is contained in:
parent
08a6188123
commit
9bbebd69dd
20
src/diag.rs
20
src/diag.rs
@ -36,8 +36,10 @@ pub type StrResult<T> = Result<T, String>;
|
|||||||
/// An error in a source file.
|
/// An error in a source file.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
/// The erroneous location in the source code.
|
/// The erroneous node in the source code.
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
/// Where in the node the error should be annotated.
|
||||||
|
pub pos: ErrorPos,
|
||||||
/// A diagnostic message describing the problem.
|
/// A diagnostic message describing the problem.
|
||||||
pub message: String,
|
pub message: String,
|
||||||
/// The trace of function calls leading to the error.
|
/// The trace of function calls leading to the error.
|
||||||
@ -49,12 +51,24 @@ impl Error {
|
|||||||
pub fn new(span: Span, message: impl Into<String>) -> Self {
|
pub fn new(span: Span, message: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
span,
|
span,
|
||||||
|
pos: ErrorPos::Full,
|
||||||
trace: vec![],
|
trace: vec![],
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Where in a node an error should be annotated.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ErrorPos {
|
||||||
|
/// At the start of the node.
|
||||||
|
Start,
|
||||||
|
/// Over the full width of the node.
|
||||||
|
Full,
|
||||||
|
/// At the end of the node.
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
/// A part of an error's [trace](Error::trace).
|
/// A part of an error's [trace](Error::trace).
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum Tracepoint {
|
pub enum Tracepoint {
|
||||||
@ -110,9 +124,7 @@ impl<T> Trace<T> for TypResult<T> {
|
|||||||
{
|
{
|
||||||
self.map_err(|mut errors| {
|
self.map_err(|mut errors| {
|
||||||
for error in errors.iter_mut() {
|
for error in errors.iter_mut() {
|
||||||
if !span.surrounds(error.span) {
|
error.trace.push(Spanned::new(make_point(), span));
|
||||||
error.trace.push(Spanned::new(make_point(), span));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
errors
|
errors
|
||||||
})
|
})
|
||||||
|
@ -75,7 +75,7 @@ impl Show for RawNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut seq = vec![];
|
let mut seq = vec![];
|
||||||
syntax::highlight_themed(&self.text, mode, &THEME, &mut |piece, style| {
|
syntax::highlight_themed(&self.text, mode, &THEME, |piece, style| {
|
||||||
seq.push(styled(piece, foreground, style));
|
seq.push(styled(piece, foreground, style));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ fn print_diagnostics(
|
|||||||
for error in errors {
|
for error in errors {
|
||||||
// The main diagnostic.
|
// The main diagnostic.
|
||||||
let diag = Diagnostic::error().with_message(error.message).with_labels(vec![
|
let diag = Diagnostic::error().with_message(error.message).with_labels(vec![
|
||||||
Label::primary(error.span.source, error.span.to_range()),
|
Label::primary(error.span.source(), sources.range(error.span)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
term::emit(&mut w, &config, sources, &diag)?;
|
term::emit(&mut w, &config, sources, &diag)?;
|
||||||
@ -249,7 +249,7 @@ fn print_diagnostics(
|
|||||||
for point in error.trace {
|
for point in error.trace {
|
||||||
let message = point.v.to_string();
|
let message = point.v.to_string();
|
||||||
let help = Diagnostic::help().with_message(message).with_labels(vec![
|
let help = Diagnostic::help().with_message(message).with_labels(vec![
|
||||||
Label::primary(point.span.source, point.span.to_range()),
|
Label::primary(point.span.source(), sources.range(point.span)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
term::emit(&mut w, &config, sources, &help)?;
|
term::emit(&mut w, &config, sources, &help)?;
|
||||||
|
@ -134,6 +134,7 @@ impl Reparser<'_> {
|
|||||||
if let SearchState::Contained(pos) = search {
|
if let SearchState::Contained(pos) = search {
|
||||||
let child = &mut node.children_mut()[pos.idx];
|
let child = &mut node.children_mut()[pos.idx];
|
||||||
let prev_len = child.len();
|
let prev_len = child.len();
|
||||||
|
let prev_count = child.count();
|
||||||
|
|
||||||
if let Some(range) = match child {
|
if let Some(range) = match child {
|
||||||
SyntaxNode::Inner(node) => {
|
SyntaxNode::Inner(node) => {
|
||||||
@ -142,7 +143,8 @@ impl Reparser<'_> {
|
|||||||
SyntaxNode::Leaf(_) => None,
|
SyntaxNode::Leaf(_) => None,
|
||||||
} {
|
} {
|
||||||
let new_len = child.len();
|
let new_len = child.len();
|
||||||
node.update_parent(new_len, prev_len);
|
let new_count = child.count();
|
||||||
|
node.update_parent(prev_len, new_len, prev_count, new_count);
|
||||||
return Some(range);
|
return Some(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,9 @@ pub use tokens::*;
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::diag::ErrorPos;
|
||||||
use crate::syntax::ast::{Associativity, BinOp, UnOp};
|
use crate::syntax::ast::{Associativity, BinOp, UnOp};
|
||||||
use crate::syntax::{ErrorPos, NodeKind, SyntaxNode};
|
use crate::syntax::{NodeKind, SyntaxNode};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Parse a source file.
|
/// Parse a source file.
|
||||||
|
@ -3,7 +3,8 @@ use std::mem;
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use super::{TokenMode, Tokens};
|
use super::{TokenMode, Tokens};
|
||||||
use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode};
|
use crate::diag::ErrorPos;
|
||||||
|
use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// A convenient token-based parser.
|
/// A convenient token-based parser.
|
||||||
|
@ -4,9 +4,10 @@ use unicode_xid::UnicodeXID;
|
|||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
|
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
|
||||||
|
use crate::diag::ErrorPos;
|
||||||
use crate::geom::{AngleUnit, LengthUnit};
|
use crate::geom::{AngleUnit, LengthUnit};
|
||||||
use crate::syntax::ast::{MathNode, RawNode, Unit};
|
use crate::syntax::ast::{MathNode, RawNode, Unit};
|
||||||
use crate::syntax::{ErrorPos, NodeKind};
|
use crate::syntax::NodeKind;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// An iterator over the tokens of a string of source code.
|
/// An iterator over the tokens of a string of source code.
|
||||||
|
@ -20,24 +20,24 @@ use codespan_reporting::files::{self, Files};
|
|||||||
|
|
||||||
/// A unique identifier for a loaded source file.
|
/// A unique identifier for a loaded source file.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct SourceId(u32);
|
pub struct SourceId(u16);
|
||||||
|
|
||||||
impl SourceId {
|
impl SourceId {
|
||||||
/// Create a new source id for a file that is not part of a store.
|
/// Create a new source id for a file that is not part of a store.
|
||||||
pub const fn detached() -> Self {
|
pub const fn detached() -> Self {
|
||||||
Self(u32::MAX)
|
Self(u16::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a source id from the raw underlying value.
|
/// Create a source id from the raw underlying value.
|
||||||
///
|
///
|
||||||
/// This should only be called with values returned by
|
/// This should only be called with values returned by
|
||||||
/// [`into_raw`](Self::into_raw).
|
/// [`into_raw`](Self::into_raw).
|
||||||
pub const fn from_raw(v: u32) -> Self {
|
pub const fn from_raw(v: u16) -> Self {
|
||||||
Self(v)
|
Self(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert into the raw underlying value.
|
/// Convert into the raw underlying value.
|
||||||
pub const fn into_raw(self) -> u32 {
|
pub const fn into_raw(self) -> u16 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ impl SourceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No existing file yet.
|
// No existing file yet.
|
||||||
let id = SourceId(self.sources.len() as u32);
|
let id = SourceId(self.sources.len() as u16);
|
||||||
self.sources.push(SourceFile::new(id, path, src));
|
self.sources.push(SourceFile::new(id, path, src));
|
||||||
|
|
||||||
// Register in file map if the path was known to the loader.
|
// Register in file map if the path was known to the loader.
|
||||||
@ -140,6 +140,13 @@ impl SourceStore {
|
|||||||
) -> Range<usize> {
|
) -> Range<usize> {
|
||||||
self.sources[id.0 as usize].edit(replace, with)
|
self.sources[id.0 as usize].edit(replace, with)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map a span that points into this source store to a byte range.
|
||||||
|
///
|
||||||
|
/// Panics if the span does not point into this source store.
|
||||||
|
pub fn range(&self, span: Span) -> Range<usize> {
|
||||||
|
self.get(span.source()).range(span)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single source file.
|
/// A single source file.
|
||||||
@ -160,10 +167,14 @@ impl SourceFile {
|
|||||||
pub fn new(id: SourceId, path: &Path, src: String) -> Self {
|
pub fn new(id: SourceId, path: &Path, src: String) -> Self {
|
||||||
let mut lines = vec![Line { byte_idx: 0, utf16_idx: 0 }];
|
let mut lines = vec![Line { byte_idx: 0, utf16_idx: 0 }];
|
||||||
lines.extend(Line::iter(0, 0, &src));
|
lines.extend(Line::iter(0, 0, &src));
|
||||||
|
|
||||||
|
let mut root = parse(&src);
|
||||||
|
root.number(id, 1);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
path: path.normalize(),
|
path: path.normalize(),
|
||||||
root: parse(&src),
|
root,
|
||||||
src,
|
src,
|
||||||
lines,
|
lines,
|
||||||
rev: 0,
|
rev: 0,
|
||||||
@ -178,8 +189,8 @@ impl SourceFile {
|
|||||||
/// Create a source file with the same synthetic span for all nodes.
|
/// Create a source file with the same synthetic span for all nodes.
|
||||||
pub fn synthesized(src: impl Into<String>, span: Span) -> Self {
|
pub fn synthesized(src: impl Into<String>, span: Span) -> Self {
|
||||||
let mut file = Self::detached(src);
|
let mut file = Self::detached(src);
|
||||||
file.root.synthesize(Arc::new(span));
|
file.root.synthesize(span);
|
||||||
file.id = span.source;
|
file.id = span.source();
|
||||||
file
|
file
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +243,7 @@ impl SourceFile {
|
|||||||
self.lines = vec![Line { byte_idx: 0, utf16_idx: 0 }];
|
self.lines = vec![Line { byte_idx: 0, utf16_idx: 0 }];
|
||||||
self.lines.extend(Line::iter(0, 0, &self.src));
|
self.lines.extend(Line::iter(0, 0, &self.src));
|
||||||
self.root = parse(&self.src);
|
self.root = parse(&self.src);
|
||||||
|
self.root.number(self.id(), 1);
|
||||||
self.rev = self.rev.wrapping_add(1);
|
self.rev = self.rev.wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +277,9 @@ impl SourceFile {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// Incrementally reparse the replaced range.
|
// Incrementally reparse the replaced range.
|
||||||
reparse(&mut self.root, &self.src, replace, with.len())
|
let range = reparse(&mut self.root, &self.src, replace, with.len());
|
||||||
|
self.root.number(self.id(), 1);
|
||||||
|
range
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the length of the file in bytes.
|
/// Get the length of the file in bytes.
|
||||||
@ -284,6 +298,15 @@ impl SourceFile {
|
|||||||
self.lines.len()
|
self.lines.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map a span that points into this source file to a byte range.
|
||||||
|
///
|
||||||
|
/// Panics if the span does not point into this source file.
|
||||||
|
pub fn range(&self, span: Span) -> Range<usize> {
|
||||||
|
self.root
|
||||||
|
.range(span, 0)
|
||||||
|
.expect("span does not point into this source file")
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the index of the UTF-16 code unit at the byte index.
|
/// Return the index of the UTF-16 code unit at the byte index.
|
||||||
pub fn byte_to_utf16(&self, byte_idx: usize) -> Option<usize> {
|
pub fn byte_to_utf16(&self, byte_idx: usize) -> Option<usize> {
|
||||||
let line_idx = self.byte_to_line(byte_idx)?;
|
let line_idx = self.byte_to_line(byte_idx)?;
|
||||||
|
@ -10,24 +10,38 @@ use crate::parse::TokenMode;
|
|||||||
|
|
||||||
/// Provide highlighting categories for the descendants of a node that fall into
|
/// Provide highlighting categories for the descendants of a node that fall into
|
||||||
/// a range.
|
/// a range.
|
||||||
pub fn highlight_node<F>(node: &SyntaxNode, range: Range<usize>, f: &mut F)
|
pub fn highlight_node<F>(root: &SyntaxNode, range: Range<usize>, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Range<usize>, Category),
|
F: FnMut(Range<usize>, Category),
|
||||||
|
{
|
||||||
|
highlight_node_impl(0, root, range, &mut f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide highlighting categories for the descendants of a node that fall into
|
||||||
|
/// a range.
|
||||||
|
pub fn highlight_node_impl<F>(
|
||||||
|
mut offset: usize,
|
||||||
|
node: &SyntaxNode,
|
||||||
|
range: Range<usize>,
|
||||||
|
f: &mut F,
|
||||||
|
) where
|
||||||
|
F: FnMut(Range<usize>, Category),
|
||||||
{
|
{
|
||||||
for (i, child) in node.children().enumerate() {
|
for (i, child) in node.children().enumerate() {
|
||||||
let span = child.span();
|
let span = offset .. offset + child.len();
|
||||||
if range.start <= span.end && range.end >= span.start {
|
if range.start <= span.end && range.end >= span.start {
|
||||||
if let Some(category) = Category::determine(child, node, i) {
|
if let Some(category) = Category::determine(child, node, i) {
|
||||||
f(span.to_range(), category);
|
f(span, category);
|
||||||
}
|
}
|
||||||
highlight_node(child, range.clone(), f);
|
highlight_node_impl(offset, child, range.clone(), f);
|
||||||
}
|
}
|
||||||
|
offset += child.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Highlight source text in a theme by calling `f` with each consecutive piece
|
/// Highlight source text in a theme by calling `f` with each consecutive piece
|
||||||
/// and its style.
|
/// and its style.
|
||||||
pub fn highlight_themed<F>(text: &str, mode: TokenMode, theme: &Theme, f: &mut F)
|
pub fn highlight_themed<F>(text: &str, mode: TokenMode, theme: &Theme, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&str, Style),
|
F: FnMut(&str, Style),
|
||||||
{
|
{
|
||||||
@ -43,12 +57,13 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let highlighter = Highlighter::new(&theme);
|
let highlighter = Highlighter::new(&theme);
|
||||||
highlight_themed_impl(text, &root, vec![], &highlighter, f);
|
highlight_themed_impl(text, 0, &root, vec![], &highlighter, &mut f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursive implementation for returning syntect styles.
|
/// Recursive implementation for returning syntect styles.
|
||||||
fn highlight_themed_impl<F>(
|
fn highlight_themed_impl<F>(
|
||||||
text: &str,
|
text: &str,
|
||||||
|
mut offset: usize,
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
highlighter: &Highlighter,
|
highlighter: &Highlighter,
|
||||||
@ -57,7 +72,7 @@ fn highlight_themed_impl<F>(
|
|||||||
F: FnMut(&str, Style),
|
F: FnMut(&str, Style),
|
||||||
{
|
{
|
||||||
if node.children().len() == 0 {
|
if node.children().len() == 0 {
|
||||||
let piece = &text[node.span().to_range()];
|
let piece = &text[offset .. offset + node.len()];
|
||||||
let style = highlighter.style_for_stack(&scopes);
|
let style = highlighter.style_for_stack(&scopes);
|
||||||
f(piece, style);
|
f(piece, style);
|
||||||
return;
|
return;
|
||||||
@ -68,7 +83,8 @@ fn highlight_themed_impl<F>(
|
|||||||
if let Some(category) = Category::determine(child, node, i) {
|
if let Some(category) = Category::determine(child, node, i) {
|
||||||
scopes.push(Scope::new(category.tm_scope()).unwrap())
|
scopes.push(Scope::new(category.tm_scope()).unwrap())
|
||||||
}
|
}
|
||||||
highlight_themed_impl(text, child, scopes, highlighter, f);
|
highlight_themed_impl(text, offset, child, scopes, highlighter, f);
|
||||||
|
offset += child.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +108,7 @@ pub fn highlight_pre(text: &str, mode: TokenMode, theme: &Theme) -> String {
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
buf.push_str("<pre>\n");
|
buf.push_str("<pre>\n");
|
||||||
|
|
||||||
highlight_themed(text, mode, theme, &mut |piece, style| {
|
highlight_themed(text, mode, theme, |piece, style| {
|
||||||
let styled = style != Style::default();
|
let styled = style != Style::default();
|
||||||
if styled {
|
if styled {
|
||||||
buf.push_str("<span style=\"");
|
buf.push_str("<span style=\"");
|
||||||
|
@ -13,7 +13,8 @@ pub use highlight::*;
|
|||||||
pub use span::*;
|
pub use span::*;
|
||||||
|
|
||||||
use self::ast::{MathNode, RawNode, TypedNode, Unit};
|
use self::ast::{MathNode, RawNode, TypedNode, Unit};
|
||||||
use crate::diag::Error;
|
use crate::diag::{Error, ErrorPos};
|
||||||
|
use crate::source::SourceId;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// An inner or leaf node in the untyped syntax tree.
|
/// An inner or leaf node in the untyped syntax tree.
|
||||||
@ -29,8 +30,8 @@ impl SyntaxNode {
|
|||||||
/// Returns the metadata of the node.
|
/// Returns the metadata of the node.
|
||||||
pub fn data(&self) -> &NodeData {
|
pub fn data(&self) -> &NodeData {
|
||||||
match self {
|
match self {
|
||||||
SyntaxNode::Inner(n) => &n.data,
|
SyntaxNode::Inner(inner) => &inner.data,
|
||||||
SyntaxNode::Leaf(t) => t,
|
SyntaxNode::Leaf(leaf) => leaf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,15 +45,31 @@ impl SyntaxNode {
|
|||||||
self.data().len()
|
self.data().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The number of descendants, including the node itself.
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
SyntaxNode::Inner(inner) => inner.count(),
|
||||||
|
SyntaxNode::Leaf(_) => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The span of the node.
|
/// The span of the node.
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
todo!()
|
self.data().span()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the span points into this node, convert it to a byte range.
|
||||||
|
pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
|
||||||
|
match self {
|
||||||
|
SyntaxNode::Inner(inner) => inner.range(span, offset),
|
||||||
|
SyntaxNode::Leaf(leaf) => leaf.range(span, offset),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The node's children.
|
/// The node's children.
|
||||||
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxNode::Inner(n) => n.children(),
|
SyntaxNode::Inner(inner) => inner.children(),
|
||||||
SyntaxNode::Leaf(_) => [].iter(),
|
SyntaxNode::Leaf(_) => [].iter(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +79,7 @@ impl SyntaxNode {
|
|||||||
/// This method is slow and only intended for testing.
|
/// This method is slow and only intended for testing.
|
||||||
pub fn leafs(&self) -> Vec<Self> {
|
pub fn leafs(&self) -> Vec<Self> {
|
||||||
if match self {
|
if match self {
|
||||||
SyntaxNode::Inner(n) => n.children().len() == 0,
|
SyntaxNode::Inner(inner) => inner.children().len() == 0,
|
||||||
SyntaxNode::Leaf(_) => true,
|
SyntaxNode::Leaf(_) => true,
|
||||||
} {
|
} {
|
||||||
vec![self.clone()]
|
vec![self.clone()]
|
||||||
@ -86,7 +103,9 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
NodeKind::Error(..) => todo!(),
|
&NodeKind::Error(pos, ref message) => {
|
||||||
|
vec![Error { pos, ..Error::new(self.span(), message) }]
|
||||||
|
}
|
||||||
_ => self
|
_ => self
|
||||||
.children()
|
.children()
|
||||||
.filter(|node| node.erroneous())
|
.filter(|node| node.erroneous())
|
||||||
@ -116,20 +135,28 @@ impl SyntaxNode {
|
|||||||
/// Change the type of the node.
|
/// Change the type of the node.
|
||||||
pub fn convert(&mut self, kind: NodeKind) {
|
pub fn convert(&mut self, kind: NodeKind) {
|
||||||
match self {
|
match self {
|
||||||
Self::Inner(node) => {
|
Self::Inner(inner) => {
|
||||||
let node = Arc::make_mut(node);
|
let node = Arc::make_mut(inner);
|
||||||
node.erroneous |= kind.is_error();
|
node.erroneous |= kind.is_error();
|
||||||
node.data.kind = kind;
|
node.data.kind = kind;
|
||||||
}
|
}
|
||||||
Self::Leaf(data) => data.kind = kind,
|
Self::Leaf(leaf) => leaf.kind = kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a synthetic span for the node and all its children.
|
/// Assign spans to each node.
|
||||||
pub fn synthesize(&mut self, span: Arc<Span>) {
|
pub fn number(&mut self, id: SourceId, from: u64) {
|
||||||
match self {
|
match self {
|
||||||
SyntaxNode::Inner(n) => Arc::make_mut(n).synthesize(span),
|
SyntaxNode::Inner(inner) => Arc::make_mut(inner).number(id, from),
|
||||||
SyntaxNode::Leaf(t) => t.synthesize(span),
|
SyntaxNode::Leaf(leaf) => leaf.number(id, from),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a synthetic node id for the node and all its descendants.
|
||||||
|
pub fn synthesize(&mut self, span: Span) {
|
||||||
|
match self {
|
||||||
|
SyntaxNode::Inner(inner) => Arc::make_mut(inner).synthesize(span),
|
||||||
|
SyntaxNode::Leaf(leaf) => leaf.synthesize(span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,10 +181,12 @@ impl Debug for SyntaxNode {
|
|||||||
pub struct InnerNode {
|
pub struct InnerNode {
|
||||||
/// Node metadata.
|
/// Node metadata.
|
||||||
data: NodeData,
|
data: NodeData,
|
||||||
/// This node's children, losslessly make up this node.
|
/// The number of nodes in the whole subtree, including this node.
|
||||||
children: Vec<SyntaxNode>,
|
count: usize,
|
||||||
/// Whether this node or any of its children are erroneous.
|
/// Whether this node or any of its children are erroneous.
|
||||||
erroneous: bool,
|
erroneous: bool,
|
||||||
|
/// This node's children, losslessly make up this node.
|
||||||
|
children: Vec<SyntaxNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InnerNode {
|
impl InnerNode {
|
||||||
@ -168,17 +197,21 @@ impl InnerNode {
|
|||||||
|
|
||||||
/// Creates a new node with the given kind and children.
|
/// Creates a new node with the given kind and children.
|
||||||
pub fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self {
|
pub fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self {
|
||||||
|
let mut len = 0;
|
||||||
|
let mut count = 1;
|
||||||
let mut erroneous = kind.is_error();
|
let mut erroneous = kind.is_error();
|
||||||
let len = children
|
|
||||||
.iter()
|
for child in &children {
|
||||||
.inspect(|c| erroneous |= c.erroneous())
|
len += child.len();
|
||||||
.map(SyntaxNode::len)
|
count += child.count();
|
||||||
.sum();
|
erroneous |= child.erroneous();
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
data: NodeData::new(kind, len),
|
data: NodeData::new(kind, len),
|
||||||
children,
|
count,
|
||||||
erroneous,
|
erroneous,
|
||||||
|
children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,16 +230,54 @@ impl InnerNode {
|
|||||||
self.data().len()
|
self.data().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The node's span.
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.data().span()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of descendants, including the node itself.
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the span points into this node, convert it to a byte range.
|
||||||
|
pub fn range(&self, span: Span, mut offset: usize) -> Option<Range<usize>> {
|
||||||
|
if let Some(range) = self.data.range(span, offset) {
|
||||||
|
return Some(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in &self.children {
|
||||||
|
if let Some(range) = child.range(span, offset) {
|
||||||
|
return Some(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += child.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// The node's children.
|
/// The node's children.
|
||||||
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
||||||
self.children.iter()
|
self.children.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a synthetic span for the node and all its children.
|
/// Assign spans to each node.
|
||||||
pub fn synthesize(&mut self, span: Arc<Span>) {
|
pub fn number(&mut self, id: SourceId, mut from: u64) {
|
||||||
self.data.synthesize(span.clone());
|
self.data.number(id, from);
|
||||||
|
from += 1;
|
||||||
|
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
child.synthesize(span.clone());
|
child.number(id, from);
|
||||||
|
from += child.count() as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a synthetic node id for the node and all its descendants.
|
||||||
|
pub fn synthesize(&mut self, span: Span) {
|
||||||
|
self.data.synthesize(span);
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.synthesize(span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,24 +293,41 @@ impl InnerNode {
|
|||||||
replacement: Vec<SyntaxNode>,
|
replacement: Vec<SyntaxNode>,
|
||||||
) {
|
) {
|
||||||
let superseded = &self.children[range.clone()];
|
let superseded = &self.children[range.clone()];
|
||||||
let superseded_len: usize = superseded.iter().map(SyntaxNode::len).sum();
|
|
||||||
let replacement_len: usize = replacement.iter().map(SyntaxNode::len).sum();
|
|
||||||
|
|
||||||
// If we're erroneous, but not due to the superseded range, then we will
|
// Compute the new byte length.
|
||||||
// still be erroneous after the replacement.
|
self.data.len = self.data.len
|
||||||
let still_erroneous =
|
+ replacement.iter().map(SyntaxNode::len).sum::<usize>()
|
||||||
self.erroneous && !superseded.iter().any(SyntaxNode::erroneous);
|
- superseded.iter().map(SyntaxNode::len).sum::<usize>();
|
||||||
|
|
||||||
|
// Compute the new descendant count.
|
||||||
|
self.count = self.count
|
||||||
|
+ replacement.iter().map(SyntaxNode::count).sum::<usize>()
|
||||||
|
- superseded.iter().map(SyntaxNode::count).sum::<usize>();
|
||||||
|
|
||||||
|
// Determine whether we're still erroneous after the replacement. That's
|
||||||
|
// the case if
|
||||||
|
// - any of the new nodes is erroneous,
|
||||||
|
// - or if we were erroneous before due to a non-superseded node.
|
||||||
|
self.erroneous = replacement.iter().any(SyntaxNode::erroneous)
|
||||||
|
|| (self.erroneous
|
||||||
|
&& (self.children[.. range.start].iter().any(SyntaxNode::erroneous)
|
||||||
|
|| self.children[range.end ..].iter().any(SyntaxNode::erroneous)));
|
||||||
|
|
||||||
|
// Perform the replacement.
|
||||||
self.children.splice(range, replacement);
|
self.children.splice(range, replacement);
|
||||||
self.data.len = self.data.len + replacement_len - superseded_len;
|
|
||||||
self.erroneous =
|
|
||||||
still_erroneous || self.children.iter().any(SyntaxNode::erroneous);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the length of this node given the old and new length of
|
/// Update the length of this node given the old and new length of
|
||||||
/// replaced children.
|
/// replaced children.
|
||||||
pub(crate) fn update_parent(&mut self, new_len: usize, old_len: usize) {
|
pub(crate) fn update_parent(
|
||||||
self.data.len = self.data.len() + new_len - old_len;
|
&mut self,
|
||||||
|
prev_len: usize,
|
||||||
|
new_len: usize,
|
||||||
|
prev_count: usize,
|
||||||
|
new_count: usize,
|
||||||
|
) {
|
||||||
|
self.data.len = self.data.len + new_len - prev_len;
|
||||||
|
self.count = self.count + new_count - prev_count;
|
||||||
self.erroneous = self.children.iter().any(SyntaxNode::erroneous);
|
self.erroneous = self.children.iter().any(SyntaxNode::erroneous);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,34 +356,51 @@ impl Debug for InnerNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Data shared between inner and leaf nodes.
|
/// Data shared between inner and leaf nodes.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct NodeData {
|
pub struct NodeData {
|
||||||
/// What kind of node this is (each kind would have its own struct in a
|
/// What kind of node this is (each kind would have its own struct in a
|
||||||
/// strongly typed AST).
|
/// strongly typed AST).
|
||||||
kind: NodeKind,
|
kind: NodeKind,
|
||||||
/// The byte length of the node in the source.
|
/// The byte length of the node in the source.
|
||||||
len: usize,
|
len: usize,
|
||||||
|
/// The node's span.
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeData {
|
impl NodeData {
|
||||||
/// Create new node metadata.
|
/// Create new node metadata.
|
||||||
pub fn new(kind: NodeKind, len: usize) -> Self {
|
pub fn new(kind: NodeKind, len: usize) -> Self {
|
||||||
Self { len, kind }
|
Self { len, kind, span: Span::detached() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type of the node.
|
/// The node's type.
|
||||||
pub fn kind(&self) -> &NodeKind {
|
pub fn kind(&self) -> &NodeKind {
|
||||||
&self.kind
|
&self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The length of the node.
|
/// The node's length.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The node's span.
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.span
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the span points into this node, convert it to a byte range.
|
||||||
|
pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
|
||||||
|
(self.span == span).then(|| offset .. offset + self.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign spans to each node.
|
||||||
|
pub fn number(&mut self, id: SourceId, from: u64) {
|
||||||
|
self.span = Span::new(id, from);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a synthetic span for the node.
|
/// Set a synthetic span for the node.
|
||||||
pub fn synthesize(&mut self, _: Arc<Span>) {
|
pub fn synthesize(&mut self, span: Span) {
|
||||||
todo!()
|
self.span = span;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +416,12 @@ impl Debug for NodeData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for NodeData {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.kind == other.kind && self.len == other.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// All syntactical building blocks that can be part of a Typst document.
|
/// All syntactical building blocks that can be part of a Typst document.
|
||||||
///
|
///
|
||||||
/// Can be emitted as a token by the tokenizer or as part of a syntax node by
|
/// Can be emitted as a token by the tokenizer or as part of a syntax node by
|
||||||
@ -547,17 +658,6 @@ pub enum NodeKind {
|
|||||||
Unknown(EcoString),
|
Unknown(EcoString),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where in a node an error should be annotated.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum ErrorPos {
|
|
||||||
/// At the start of the node.
|
|
||||||
Start,
|
|
||||||
/// Over the full width of the node.
|
|
||||||
Full,
|
|
||||||
/// At the end of the node.
|
|
||||||
End,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeKind {
|
impl NodeKind {
|
||||||
/// Whether this is some kind of brace.
|
/// Whether this is some kind of brace.
|
||||||
pub fn is_brace(&self) -> bool {
|
pub fn is_brace(&self) -> bool {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use std::cmp::Ordering;
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::ops::Range;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use crate::source::SourceId;
|
use crate::syntax::SourceId;
|
||||||
|
|
||||||
/// A value with the span it corresponds to in the source code.
|
/// A value with the span it corresponds to in the source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -35,122 +34,52 @@ impl<T> Spanned<T> {
|
|||||||
|
|
||||||
impl<T: Debug> Debug for Spanned<T> {
|
impl<T: Debug> Debug for Spanned<T> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
self.v.fmt(f)?;
|
self.v.fmt(f)
|
||||||
if f.alternate() {
|
|
||||||
f.write_str(" <")?;
|
|
||||||
self.span.fmt(f)?;
|
|
||||||
f.write_str(">")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bounds of a slice of source code.
|
/// A unique identifier for a syntax node.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
///
|
||||||
pub struct Span {
|
/// This is used throughout the compiler to track which source section an error
|
||||||
/// The id of the source file.
|
/// or element stems from. Can be mapped back to a source id + byte range for
|
||||||
pub source: SourceId,
|
/// user facing display.
|
||||||
/// The inclusive start position.
|
///
|
||||||
pub start: usize,
|
/// Node ids are ordered in the tree to enable quickly finding the node with
|
||||||
/// The inclusive end position.
|
/// some id:
|
||||||
pub end: usize,
|
/// - The id of a parent is always smaller than the ids of any of its children.
|
||||||
}
|
/// - The id of a node is always greater than any id in the subtrees of any left
|
||||||
|
/// sibling and smaller than any id the subtrees of any right sibling.
|
||||||
|
///
|
||||||
|
/// Node ids stay mostly stable, even for nodes behind an insertion. This is not
|
||||||
|
/// true for simple spans/ranges as they shift. Node ids can be used as inputs
|
||||||
|
/// to memoized functions without hurting cache performance when text is
|
||||||
|
/// inserted somewhere in the document other than the end.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct Span(NonZeroU64);
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
/// Create a new span from start and end positions.
|
/// Create a new span from a source id and a unique number.
|
||||||
pub fn new(source: SourceId, start: usize, end: usize) -> Self {
|
pub const fn new(id: SourceId, number: u64) -> Self {
|
||||||
Self { source, start, end }
|
assert!(number > 0 && number < (1 << 48));
|
||||||
|
let bits = ((id.into_raw() as u64) << 48) | number;
|
||||||
|
Self(nonzero(bits))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a span including just a single position.
|
/// A node that does not belong to any source file.
|
||||||
pub fn at(source: SourceId, pos: usize) -> Self {
|
pub const fn detached() -> Self {
|
||||||
Self::new(source, pos, pos)
|
Self(nonzero(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a span without real location information, usually for testing.
|
/// The id of the source file the span points into.
|
||||||
pub fn detached() -> Self {
|
pub const fn source(self) -> SourceId {
|
||||||
Self {
|
SourceId::from_raw((self.0.get() >> 48) as u16)
|
||||||
source: SourceId::from_raw(0),
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a span with a different start position.
|
|
||||||
pub fn with_start(self, start: usize) -> Self {
|
|
||||||
Self { start, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a span with a different end position.
|
|
||||||
pub fn with_end(self, end: usize) -> Self {
|
|
||||||
Self { end, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the span is a single point.
|
|
||||||
pub fn is_empty(self) -> bool {
|
|
||||||
self.start == self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The byte length of the spanned region.
|
|
||||||
pub fn len(self) -> usize {
|
|
||||||
self.end - self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A new span at the position of this span's start.
|
|
||||||
pub fn at_start(&self) -> Span {
|
|
||||||
Self::at(self.source, self.start)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A new span at the position of this span's end.
|
|
||||||
pub fn at_end(&self) -> Span {
|
|
||||||
Self::at(self.source, self.end)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new span with the earlier start and later end position.
|
|
||||||
///
|
|
||||||
/// This panics if the spans come from different files.
|
|
||||||
pub fn join(self, other: Self) -> Self {
|
|
||||||
debug_assert_eq!(self.source, other.source);
|
|
||||||
Self {
|
|
||||||
source: self.source,
|
|
||||||
start: self.start.min(other.start),
|
|
||||||
end: self.end.max(other.end),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expand a span by merging it with another span.
|
|
||||||
pub fn expand(&mut self, other: Self) {
|
|
||||||
*self = self.join(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test whether a position is within the span.
|
|
||||||
pub fn contains(&self, pos: usize) -> bool {
|
|
||||||
self.start <= pos && self.end >= pos
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test whether one span complete contains the other span.
|
|
||||||
pub fn surrounds(self, other: Self) -> bool {
|
|
||||||
self.source == other.source && self.start <= other.start && self.end >= other.end
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to a `Range<usize>` for indexing.
|
|
||||||
pub fn to_range(self) -> Range<usize> {
|
|
||||||
self.start .. self.end
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Span {
|
/// Convert to a non zero u64.
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
const fn nonzero(v: u64) -> NonZeroU64 {
|
||||||
write!(f, "{:?}-{:?}", self.start, self.end)
|
match NonZeroU64::new(v) {
|
||||||
}
|
Some(v) => v,
|
||||||
}
|
None => unreachable!(),
|
||||||
|
|
||||||
impl PartialOrd for Span {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
if self.source == other.source {
|
|
||||||
Some(self.start.cmp(&other.start).then(self.end.cmp(&other.end)))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use tiny_skia as sk;
|
|||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use typst::diag::Error;
|
use typst::diag::ErrorPos;
|
||||||
use typst::eval::{Smart, Value};
|
use typst::eval::{Smart, Value};
|
||||||
use typst::frame::{Element, Frame};
|
use typst::frame::{Element, Frame};
|
||||||
use typst::geom::{Length, RgbaColor, Sides};
|
use typst::geom::{Length, RgbaColor, Sides};
|
||||||
@ -18,7 +18,6 @@ use typst::library::text::{TextNode, TextSize};
|
|||||||
use typst::loading::FsLoader;
|
use typst::loading::FsLoader;
|
||||||
use typst::model::StyleMap;
|
use typst::model::StyleMap;
|
||||||
use typst::source::SourceFile;
|
use typst::source::SourceFile;
|
||||||
use typst::syntax::Span;
|
|
||||||
use typst::{bail, Config, Context};
|
use typst::{bail, Config, Context};
|
||||||
|
|
||||||
const TYP_DIR: &str = "./typ";
|
const TYP_DIR: &str = "./typ";
|
||||||
@ -301,7 +300,7 @@ fn test_part(
|
|||||||
|
|
||||||
ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
|
ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
|
||||||
|
|
||||||
let (mut frames, mut errors) = match typst::typeset(ctx, id) {
|
let (mut frames, errors) = match typst::typeset(ctx, id) {
|
||||||
Ok(frames) => (frames, vec![]),
|
Ok(frames) => (frames, vec![]),
|
||||||
Err(errors) => (vec![], *errors),
|
Err(errors) => (vec![], *errors),
|
||||||
};
|
};
|
||||||
@ -311,15 +310,24 @@ fn test_part(
|
|||||||
frames.clear();
|
frames.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Also handle errors from other files.
|
// Map errors to range and message format, discard traces and errors from
|
||||||
errors.retain(|error| error.span.source == id);
|
// other files.
|
||||||
for error in &mut errors {
|
let mut errors: Vec<_> = errors
|
||||||
error.trace.clear();
|
.into_iter()
|
||||||
}
|
.filter(|error| error.span.source() == id)
|
||||||
|
.map(|error| {
|
||||||
|
let mut range = ctx.sources.range(error.span);
|
||||||
|
match error.pos {
|
||||||
|
ErrorPos::Start => range.end = range.start,
|
||||||
|
ErrorPos::Full => {}
|
||||||
|
ErrorPos::End => range.start = range.end,
|
||||||
|
}
|
||||||
|
(range, error.message)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// The comparison never fails since all spans are from the same source file.
|
errors.sort_by_key(|error| error.0.start);
|
||||||
ref_errors.sort_by(|a, b| a.span.partial_cmp(&b.span).unwrap());
|
ref_errors.sort_by_key(|error| error.0.start);
|
||||||
errors.sort_by(|a, b| a.span.partial_cmp(&b.span).unwrap());
|
|
||||||
|
|
||||||
if errors != ref_errors {
|
if errors != ref_errors {
|
||||||
println!(" Subtest {i} does not match expected errors. ❌");
|
println!(" Subtest {i} does not match expected errors. ❌");
|
||||||
@ -327,7 +335,7 @@ fn test_part(
|
|||||||
|
|
||||||
let source = ctx.sources.get(id);
|
let source = ctx.sources.get(id);
|
||||||
for error in errors.iter() {
|
for error in errors.iter() {
|
||||||
if error.span.source == id && !ref_errors.contains(error) {
|
if !ref_errors.contains(error) {
|
||||||
print!(" Not annotated | ");
|
print!(" Not annotated | ");
|
||||||
print_error(&source, line, error);
|
print_error(&source, line, error);
|
||||||
}
|
}
|
||||||
@ -344,7 +352,7 @@ fn test_part(
|
|||||||
(ok, compare_ref, frames)
|
(ok, compare_ref, frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_metadata(source: &SourceFile) -> (Option<bool>, Vec<Error>) {
|
fn parse_metadata(source: &SourceFile) -> (Option<bool>, Vec<(Range<usize>, String)>) {
|
||||||
let mut compare_ref = None;
|
let mut compare_ref = None;
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
|
|
||||||
@ -382,23 +390,24 @@ fn parse_metadata(source: &SourceFile) -> (Option<bool>, Vec<Error>) {
|
|||||||
let mut s = Scanner::new(rest);
|
let mut s = Scanner::new(rest);
|
||||||
let start = pos(&mut s);
|
let start = pos(&mut s);
|
||||||
let end = if s.eat_if('-') { pos(&mut s) } else { start };
|
let end = if s.eat_if('-') { pos(&mut s) } else { start };
|
||||||
let span = Span::new(source.id(), start, end);
|
let range = start .. end;
|
||||||
|
|
||||||
errors.push(Error::new(span, s.after().trim()));
|
errors.push((range, s.after().trim().to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
(compare_ref, errors)
|
(compare_ref, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_error(source: &SourceFile, line: usize, error: &Error) {
|
fn print_error(
|
||||||
let start_line = 1 + line + source.byte_to_line(error.span.start).unwrap();
|
source: &SourceFile,
|
||||||
let start_col = 1 + source.byte_to_column(error.span.start).unwrap();
|
line: usize,
|
||||||
let end_line = 1 + line + source.byte_to_line(error.span.end).unwrap();
|
(range, message): &(Range<usize>, String),
|
||||||
let end_col = 1 + source.byte_to_column(error.span.end).unwrap();
|
) {
|
||||||
println!(
|
let start_line = 1 + line + source.byte_to_line(range.start).unwrap();
|
||||||
"Error: {start_line}:{start_col}-{end_line}:{end_col}: {}",
|
let start_col = 1 + source.byte_to_column(range.start).unwrap();
|
||||||
error.message,
|
let end_line = 1 + line + source.byte_to_line(range.end).unwrap();
|
||||||
);
|
let end_col = 1 + source.byte_to_column(range.end).unwrap();
|
||||||
|
println!("Error: {start_line}:{start_col}-{end_line}:{end_col}: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pseudorandomly edit the source file and test whether a reparse produces the
|
/// Pseudorandomly edit the source file and test whether a reparse produces the
|
||||||
@ -487,10 +496,11 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool {
|
|||||||
ok &= apply(start .. end, supplement);
|
ok &= apply(start .. end, supplement);
|
||||||
}
|
}
|
||||||
|
|
||||||
let leafs = typst::parse::parse(src).leafs();
|
let source = SourceFile::detached(src);
|
||||||
let leaf_start = leafs[pick(0 .. leafs.len())].span().start;
|
let leafs = source.root().leafs();
|
||||||
|
let start = source.range(leafs[pick(0 .. leafs.len())].span()).start;
|
||||||
let supplement = supplements[pick(0 .. supplements.len())];
|
let supplement = supplements[pick(0 .. supplements.len())];
|
||||||
ok &= apply(leaf_start .. leaf_start, supplement);
|
ok &= apply(start .. start, supplement);
|
||||||
|
|
||||||
ok
|
ok
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user