summaryrefslogtreecommitdiff
path: root/src/parse/incremental.rs
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2022-06-01 16:57:38 +0200
committerGitHub <noreply@github.com>2022-06-01 16:57:38 +0200
commita937462491a63f5cff3551b5bb8bc45fb350f0b6 (patch)
tree7916fca31328fef3e21d3bd62eca132369da81b0 /src/parse/incremental.rs
parent665ed12825918bd02a6d6dbcb67860a83dd41600 (diff)
parentaf10b08cc1bd5ef78c5c048a0cbc83b123f1ffd4 (diff)
Merge pull request #73 from typst/span-numbers
Diffstat (limited to 'src/parse/incremental.rs')
-rw-r--r--src/parse/incremental.rs96
1 files changed, 55 insertions, 41 deletions
diff --git a/src/parse/incremental.rs b/src/parse/incremental.rs
index 26f97c52..59dbebeb 100644
--- a/src/parse/incremental.rs
+++ b/src/parse/incremental.rs
@@ -1,36 +1,41 @@
use std::ops::Range;
use std::sync::Arc;
-use crate::syntax::{Green, GreenNode, NodeKind};
+use crate::syntax::{InnerNode, NodeKind, Span, SyntaxNode};
use super::{
is_newline, parse, reparse_code_block, reparse_content_block, reparse_markup_elements,
};
-/// Refresh the given green node with as little parsing as possible.
+/// Refresh the given syntax node with as little parsing as possible.
///
/// Takes the new source, the range in the old source that was replaced and the
/// length of the replacement.
///
/// Returns the range in the new source that was ultimately reparsed.
pub fn reparse(
- green: &mut Arc<GreenNode>,
+ root: &mut SyntaxNode,
src: &str,
replaced: Range<usize>,
replacement_len: usize,
) -> Range<usize> {
- Reparser { src, replaced, replacement_len }
- .reparse_step(Arc::make_mut(green), 0, true)
- .unwrap_or_else(|| {
- *green = parse(src);
- 0 .. src.len()
- })
+ if let SyntaxNode::Inner(inner) = root {
+ let reparser = Reparser { src, replaced, replacement_len };
+ if let Some(range) = reparser.reparse_step(Arc::make_mut(inner), 0, true) {
+ return range;
+ }
+ }
+
+ let id = root.span().source();
+ *root = parse(src);
+ root.numberize(id, Span::FULL).unwrap();
+ 0 .. src.len()
}
-/// Allows partial refreshs of the [`Green`] node tree.
+/// Allows partial refreshs of the syntax tree.
///
/// This struct holds a description of a change. Its methods can be used to try
-/// and apply the change to a green tree.
+/// and apply the change to a syntax tree.
struct Reparser<'a> {
/// The new source code, with the change applied.
src: &'a str,
@@ -44,12 +49,12 @@ impl Reparser<'_> {
/// Try to reparse inside the given node.
fn reparse_step(
&self,
- green: &mut GreenNode,
+ node: &mut InnerNode,
mut offset: usize,
outermost: bool,
) -> Option<Range<usize>> {
- let is_markup = matches!(green.kind(), NodeKind::Markup(_));
- let original_count = green.children().len();
+ let is_markup = matches!(node.kind(), NodeKind::Markup(_));
+ let original_count = node.children().len();
let original_offset = offset;
let mut search = SearchState::default();
@@ -62,9 +67,10 @@ impl Reparser<'_> {
let mut child_outermost = false;
// Find the the first child in the range of children to reparse.
- for (i, child) in green.children().iter().enumerate() {
- let pos = GreenPos { idx: i, offset };
+ for (i, child) in node.children().enumerate() {
+ let pos = NodePos { idx: i, offset };
let child_span = offset .. offset + child.len();
+ child_outermost = outermost && i + 1 == original_count;
match search {
SearchState::NoneFound => {
@@ -81,6 +87,11 @@ impl Reparser<'_> {
};
} else if child_span.contains(&self.replaced.start) {
search = SearchState::Inside(pos);
+ } else if child_span.end == self.replaced.start
+ && self.replaced.start == self.replaced.end
+ && child_outermost
+ {
+ search = SearchState::SpanFound(pos, pos);
} else {
// We look only for non spaces, non-semicolon and also
// reject text that points to the special case for URL
@@ -112,7 +123,6 @@ impl Reparser<'_> {
}
offset += child.len();
- child_outermost = outermost && i + 1 == original_count;
if search.done().is_some() {
break;
@@ -122,24 +132,26 @@ impl Reparser<'_> {
// If we were looking for a non-whitespace element and hit the end of
// the file here, we instead use EOF as the end of the span.
if let SearchState::RequireNonTrivia(start) = search {
- search = SearchState::SpanFound(start, GreenPos {
- idx: green.children().len() - 1,
- offset: offset - green.children().last().unwrap().len(),
+ search = SearchState::SpanFound(start, NodePos {
+ idx: node.children().len() - 1,
+ offset: offset - node.children().last().unwrap().len(),
})
}
if let SearchState::Contained(pos) = search {
- let child = &mut green.children_mut()[pos.idx];
+ let child = &mut node.children_mut()[pos.idx];
let prev_len = child.len();
+ let prev_descendants = child.descendants();
if let Some(range) = match child {
- Green::Node(node) => {
+ SyntaxNode::Inner(node) => {
self.reparse_step(Arc::make_mut(node), pos.offset, child_outermost)
}
- Green::Token(_) => None,
+ SyntaxNode::Leaf(_) => None,
} {
let new_len = child.len();
- green.update_parent(new_len, prev_len);
+ let new_descendants = child.descendants();
+ node.update_parent(prev_len, new_len, prev_descendants, new_descendants);
return Some(range);
}
@@ -154,7 +166,7 @@ impl Reparser<'_> {
// treat it as a markup element.
if let Some(func) = func {
if let Some(result) = self.replace(
- green,
+ node,
func,
pos.idx .. pos.idx + 1,
superseded_span,
@@ -166,14 +178,14 @@ impl Reparser<'_> {
}
// Save the current indent if this is a markup node and stop otherwise.
- let indent = match green.kind() {
+ let indent = match node.kind() {
NodeKind::Markup(n) => *n,
_ => return None,
};
let (mut start, end) = search.done()?;
if let Some((ahead, ahead_at_start)) = ahead_nontrivia {
- let ahead_kind = green.children()[ahead.idx].kind();
+ let ahead_kind = node.children().as_slice()[ahead.idx].kind();
if start.offset == self.replaced.start
|| ahead_kind.only_at_start()
@@ -183,14 +195,14 @@ impl Reparser<'_> {
at_start = ahead_at_start;
}
} else {
- start = GreenPos { idx: 0, offset: original_offset };
+ start = NodePos { idx: 0, offset: original_offset };
}
let superseded_span =
- start.offset .. end.offset + green.children()[end.idx].len();
+ start.offset .. end.offset + node.children().as_slice()[end.idx].len();
self.replace(
- green,
+ node,
ReparseMode::MarkupElements(at_start, indent),
start.idx .. end.idx + 1,
superseded_span,
@@ -200,7 +212,7 @@ impl Reparser<'_> {
fn replace(
&self,
- green: &mut GreenNode,
+ node: &mut InnerNode,
mode: ReparseMode,
superseded_idx: Range<usize>,
superseded_span: Range<usize>,
@@ -237,7 +249,7 @@ impl Reparser<'_> {
&self.src[newborn_span.start ..],
newborn_span.len(),
differential,
- &green.children()[superseded_start ..],
+ &node.children().as_slice()[superseded_start ..],
at_start,
indent,
),
@@ -249,14 +261,16 @@ impl Reparser<'_> {
return None;
}
- green.replace_children(superseded_start .. superseded_start + amount, newborns);
+ node.replace_children(superseded_start .. superseded_start + amount, newborns)
+ .ok()?;
+
Some(newborn_span)
}
}
-/// The position of a green node.
+/// The position of a syntax node.
#[derive(Clone, Copy, Debug, PartialEq)]
-struct GreenPos {
+struct NodePos {
/// The index in the parent node.
idx: usize,
/// The byte offset in the string.
@@ -272,15 +286,15 @@ enum SearchState {
NoneFound,
/// The search has concluded by finding a node that fully contains the
/// modifications.
- Contained(GreenPos),
+ Contained(NodePos),
/// The search has found the start of the modified nodes.
- Inside(GreenPos),
+ Inside(NodePos),
/// The search has found the end of the modified nodes but the change
/// touched its boundries so another non-trivia node is needed.
- RequireNonTrivia(GreenPos),
+ RequireNonTrivia(NodePos),
/// The search has concluded by finding a start and an end index for nodes
/// with a pending reparse.
- SpanFound(GreenPos, GreenPos),
+ SpanFound(NodePos, NodePos),
}
impl Default for SearchState {
@@ -290,7 +304,7 @@ impl Default for SearchState {
}
impl SearchState {
- fn done(self) -> Option<(GreenPos, GreenPos)> {
+ fn done(self) -> Option<(NodePos, NodePos)> {
match self {
Self::NoneFound => None,
Self::Contained(s) => Some((s, s)),
@@ -341,7 +355,7 @@ mod tests {
test("a\nb\nc *hel a b lo* d\nd\ne", 13..13, "c ", 6..20);
test("~~ {a} ~~", 4 .. 5, "b", 3 .. 6);
test("{(0, 1, 2)}", 5 .. 6, "11pt", 0..14);
- test("\n= A heading", 3 .. 3, "n evocative", 3 .. 23);
+ test("\n= A heading", 4 .. 4, "n evocative", 3 .. 23);
test("for~your~thing", 9 .. 9, "a", 4 .. 15);
test("a your thing a", 6 .. 7, "a", 0 .. 14);
test("{call(); abc}", 7 .. 7, "[]", 0 .. 15);