mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
Improve span stability after incremental parsing
This commit is contained in:
parent
163c2e1aa2
commit
7a46a85d3e
@ -164,6 +164,16 @@ impl SyntaxNode {
|
|||||||
Repr::Error(node) => Arc::make_mut(node).error.span = span,
|
Repr::Error(node) => Arc::make_mut(node).error.span = span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the two syntax nodes are the same apart from spans.
|
||||||
|
pub fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
|
match (&self.0, &other.0) {
|
||||||
|
(Repr::Leaf(a), Repr::Leaf(b)) => a.spanless_eq(b),
|
||||||
|
(Repr::Inner(a), Repr::Inner(b)) => a.spanless_eq(b),
|
||||||
|
(Repr::Error(a), Repr::Error(b)) => a.spanless_eq(b),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxNode {
|
impl SyntaxNode {
|
||||||
@ -326,6 +336,11 @@ impl LeafNode {
|
|||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.text.len()
|
self.text.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the two leaf nodes are the same apart from spans.
|
||||||
|
fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
|
self.kind == other.kind && self.text == other.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for LeafNode {
|
impl Debug for LeafNode {
|
||||||
@ -440,6 +455,20 @@ impl InnerNode {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the two inner nodes are the same apart from spans.
|
||||||
|
fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
|
self.kind == other.kind
|
||||||
|
&& self.len == other.len
|
||||||
|
&& self.descendants == other.descendants
|
||||||
|
&& self.erroneous == other.erroneous
|
||||||
|
&& self.children.len() == other.children.len()
|
||||||
|
&& self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.zip(&other.children)
|
||||||
|
.all(|(a, b)| a.spanless_eq(b))
|
||||||
|
}
|
||||||
|
|
||||||
/// Replaces a range of children with a replacement.
|
/// Replaces a range of children with a replacement.
|
||||||
///
|
///
|
||||||
/// May have mutated the children if it returns `Err(_)`.
|
/// May have mutated the children if it returns `Err(_)`.
|
||||||
@ -449,6 +478,30 @@ impl InnerNode {
|
|||||||
replacement: Vec<SyntaxNode>,
|
replacement: Vec<SyntaxNode>,
|
||||||
) -> NumberingResult {
|
) -> NumberingResult {
|
||||||
let Some(id) = self.span.id() else { return Err(Unnumberable) };
|
let Some(id) = self.span.id() else { return Err(Unnumberable) };
|
||||||
|
let mut replacement_range = 0..replacement.len();
|
||||||
|
|
||||||
|
// Trim off common prefix.
|
||||||
|
while range.start < range.end
|
||||||
|
&& replacement_range.start < replacement_range.end
|
||||||
|
&& self.children[range.start]
|
||||||
|
.spanless_eq(&replacement[replacement_range.start])
|
||||||
|
{
|
||||||
|
range.start += 1;
|
||||||
|
replacement_range.start += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim off common suffix.
|
||||||
|
while range.start < range.end
|
||||||
|
&& replacement_range.start < replacement_range.end
|
||||||
|
&& self.children[range.end - 1]
|
||||||
|
.spanless_eq(&replacement[replacement_range.end - 1])
|
||||||
|
{
|
||||||
|
range.end -= 1;
|
||||||
|
replacement_range.end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut replacement_vec = replacement;
|
||||||
|
let replacement = &replacement_vec[replacement_range.clone()];
|
||||||
let superseded = &self.children[range.clone()];
|
let superseded = &self.children[range.clone()];
|
||||||
|
|
||||||
// Compute the new byte length.
|
// Compute the new byte length.
|
||||||
@ -470,9 +523,9 @@ impl InnerNode {
|
|||||||
|| self.children[range.end..].iter().any(SyntaxNode::erroneous));
|
|| self.children[range.end..].iter().any(SyntaxNode::erroneous));
|
||||||
|
|
||||||
// Perform the replacement.
|
// Perform the replacement.
|
||||||
let replacement_count = replacement.len();
|
self.children
|
||||||
self.children.splice(range.clone(), replacement);
|
.splice(range.clone(), replacement_vec.drain(replacement_range.clone()));
|
||||||
range.end = range.start + replacement_count;
|
range.end = range.start + replacement_range.len();
|
||||||
|
|
||||||
// Renumber the new children. Retries until it works, taking
|
// Renumber the new children. Retries until it works, taking
|
||||||
// exponentially more children into account.
|
// exponentially more children into account.
|
||||||
@ -577,6 +630,11 @@ impl ErrorNode {
|
|||||||
fn hint(&mut self, hint: impl Into<EcoString>) {
|
fn hint(&mut self, hint: impl Into<EcoString>) {
|
||||||
self.error.hints.push(hint.into());
|
self.error.hints.push(hint.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the two leaf nodes are the same apart from spans.
|
||||||
|
fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
|
self.text == other.text && self.error.spanless_eq(&other.error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for ErrorNode {
|
impl Debug for ErrorNode {
|
||||||
@ -597,6 +655,13 @@ pub struct SyntaxError {
|
|||||||
pub hints: Vec<EcoString>,
|
pub hints: Vec<EcoString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SyntaxError {
|
||||||
|
/// Whether the two errors are the same apart from spans.
|
||||||
|
fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
|
self.message == other.message && self.hints == other.hints
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A syntax node in a context.
|
/// A syntax node in a context.
|
||||||
///
|
///
|
||||||
/// Knows its exact offset in the file and provides access to its
|
/// Knows its exact offset in the file and provides access to its
|
||||||
|
@ -25,7 +25,7 @@ use typst::doc::{Document, Frame, FrameItem, Meta};
|
|||||||
use typst::eval::{eco_format, func, Bytes, Datetime, Library, NoneValue, Tracer, Value};
|
use typst::eval::{eco_format, func, Bytes, Datetime, Library, NoneValue, Tracer, Value};
|
||||||
use typst::font::{Font, FontBook};
|
use typst::font::{Font, FontBook};
|
||||||
use typst::geom::{Abs, Color, Smart};
|
use typst::geom::{Abs, Color, Smart};
|
||||||
use typst::syntax::{FileId, PackageVersion, Source, Span, SyntaxNode, VirtualPath};
|
use typst::syntax::{FileId, PackageVersion, Source, SyntaxNode, VirtualPath};
|
||||||
use typst::{World, WorldExt};
|
use typst::{World, WorldExt};
|
||||||
use typst_library::layout::{Margin, PageElem};
|
use typst_library::layout::{Margin, PageElem};
|
||||||
use typst_library::text::{TextElem, TextSize};
|
use typst_library::text::{TextElem, TextSize};
|
||||||
@ -779,7 +779,6 @@ fn test_reparse(
|
|||||||
];
|
];
|
||||||
|
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
|
|
||||||
let mut apply = |replace: Range<usize>, with| {
|
let mut apply = |replace: Range<usize>, with| {
|
||||||
let mut incr_source = Source::detached(text);
|
let mut incr_source = Source::detached(text);
|
||||||
if incr_source.root().len() != text.len() {
|
if incr_source.root().len() != text.len() {
|
||||||
@ -795,18 +794,14 @@ fn test_reparse(
|
|||||||
|
|
||||||
let edited_src = incr_source.text();
|
let edited_src = incr_source.text();
|
||||||
let ref_source = Source::detached(edited_src);
|
let ref_source = Source::detached(edited_src);
|
||||||
let mut ref_root = ref_source.root().clone();
|
let ref_root = ref_source.root();
|
||||||
let mut incr_root = incr_source.root().clone();
|
let incr_root = incr_source.root();
|
||||||
|
|
||||||
// Ensures that the span numbering invariants hold.
|
// Ensures that the span numbering invariants hold.
|
||||||
let spans_ok = test_spans(output, &ref_root) && test_spans(output, &incr_root);
|
let spans_ok = test_spans(output, ref_root) && test_spans(output, incr_root);
|
||||||
|
|
||||||
// Remove all spans so that the comparison works out.
|
// Ensure that the reference and incremental trees are the same.
|
||||||
let tree_ok = {
|
let tree_ok = ref_root.spanless_eq(incr_root);
|
||||||
ref_root.synthesize(Span::detached());
|
|
||||||
incr_root.synthesize(Span::detached());
|
|
||||||
ref_root == incr_root
|
|
||||||
};
|
|
||||||
|
|
||||||
if !tree_ok {
|
if !tree_ok {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user