summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-05-04 11:52:01 +0200
committerLaurenz <laurmaedje@gmail.com>2023-05-04 11:52:01 +0200
commitd9ba84085e36036409c919cff2e3eb3d126e3bb3 (patch)
tree93009915dfbb32d0dd5d387591381bdeab34f218 /src/syntax
parentbc1795732b6f7a9fb777ead590baaf831758a1f3 (diff)
More efficient incremental parsing
If validation fails, we now do exponential fallback to a larger segment instead of giving up entirely.
Diffstat (limited to 'src/syntax')
-rw-r--r--src/syntax/reparser.rs54
1 files changed, 42 insertions, 12 deletions
diff --git a/src/syntax/reparser.rs b/src/syntax/reparser.rs
index f1c606e0..c744eeb2 100644
--- a/src/syntax/reparser.rs
+++ b/src/syntax/reparser.rs
@@ -96,14 +96,22 @@ fn try_reparse(
// possible if the markup is top-level or contained in a block, not if it is
// contained in things like headings or lists because too much can go wrong
// with indent and line breaks.
- if node.kind() == SyntaxKind::Markup
- && (parent_kind.is_none() || parent_kind == Some(SyntaxKind::ContentBlock))
- && !overlap.is_empty()
+ if overlap.is_empty()
+ || node.kind() != SyntaxKind::Markup
+ || !matches!(parent_kind, None | Some(SyntaxKind::ContentBlock))
{
+ return None;
+ }
+
+ let children = node.children_mut();
+
+ // Reparse a segment. Retries until it works, taking exponentially more
+ // children into account.
+ let mut expansion = 1;
+ loop {
// Add slack in both directions.
- let children = node.children_mut();
- let mut start = overlap.start.saturating_sub(2);
- let mut end = (overlap.end + 1).min(children.len());
+ let mut start = overlap.start.saturating_sub(expansion.max(2));
+ let mut end = (overlap.end + expansion).min(children.len());
// Expand to the left.
while start > 0 && expand(&children[start]) {
@@ -141,26 +149,48 @@ fn try_reparse(
next_nesting(child, &mut prev_nesting_after);
}
+ // Determine the range in the new text that we want to reparse.
let shifted = offset + prefix_len;
let new_len = prev_len + replacement_len - replaced.len();
let new_range = shifted..shifted + new_len;
+ let at_end = end == children.len();
+
+ // Stop parsing early if this kind is encountered.
let stop_kind = match parent_kind {
Some(_) => SyntaxKind::RightBracket,
None => SyntaxKind::Eof,
};
- if let Some(newborns) =
- reparse_markup(text, new_range.clone(), &mut at_start, &mut nesting, |kind| {
- kind == stop_kind
- })
- {
- if at_start == prev_at_start_after && nesting == prev_nesting_after {
+ // Reparse!
+ let reparsed = reparse_markup(
+ text,
+ new_range.clone(),
+ &mut at_start,
+ &mut nesting,
+ |kind| kind == stop_kind,
+ );
+
+ if let Some(newborns) = reparsed {
+ // If more children follow, at_start must match its previous value.
+ // Similarly, if we children follow or we not top-level the nesting
+ // must match its previous value.
+ if (at_end || at_start == prev_at_start_after)
+ && ((at_end && parent_kind.is_none()) || nesting == prev_nesting_after)
+ {
return node
.replace_children(start..end, newborns)
.is_ok()
.then_some(new_range);
}
}
+
+ // If it didn't even work with all children, we give up.
+ if start == 0 && at_end {
+ break;
+ }
+
+ // Exponential expansion to both sides.
+ expansion *= 2;
}
None