diff options
| author | Martin Haug <mhaug@live.de> | 2022-06-01 16:57:38 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-01 16:57:38 +0200 |
| commit | a937462491a63f5cff3551b5bb8bc45fb350f0b6 (patch) | |
| tree | 7916fca31328fef3e21d3bd62eca132369da81b0 /src/parse/incremental.rs | |
| parent | 665ed12825918bd02a6d6dbcb67860a83dd41600 (diff) | |
| parent | af10b08cc1bd5ef78c5c048a0cbc83b123f1ffd4 (diff) | |
Merge pull request #73 from typst/span-numbers
Diffstat (limited to 'src/parse/incremental.rs')
| -rw-r--r-- | src/parse/incremental.rs | 96 |
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); |
