mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Introduce incremental parsing
This commit is contained in:
parent
52761a3baa
commit
1e4cab393e
@ -25,6 +25,20 @@ pub fn parse(src: &str) -> Rc<GreenNode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a block. Returns `Some` if there was only one block.
|
||||||
|
pub fn parse_block(source: &str) -> Option<Rc<GreenNode>> {
|
||||||
|
let mut p = Parser::new(source);
|
||||||
|
block(&mut p);
|
||||||
|
if p.eof() {
|
||||||
|
match p.finish().into_iter().next() {
|
||||||
|
Some(Green::Node(node)) => Some(node),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse markup.
|
/// Parse markup.
|
||||||
fn markup(p: &mut Parser) {
|
fn markup(p: &mut Parser) {
|
||||||
markup_while(p, true, &mut |_| true)
|
markup_while(p, true, &mut |_| true)
|
||||||
|
@ -268,7 +268,7 @@ impl SourceFile {
|
|||||||
/// This panics if the `replace` range is out of bounds.
|
/// This panics if the `replace` range is out of bounds.
|
||||||
pub fn edit(&mut self, replace: Range<usize>, with: &str) {
|
pub fn edit(&mut self, replace: Range<usize>, with: &str) {
|
||||||
let start = replace.start;
|
let start = replace.start;
|
||||||
self.src.replace_range(replace, with);
|
self.src.replace_range(replace.clone(), with);
|
||||||
|
|
||||||
// Remove invalidated line starts.
|
// Remove invalidated line starts.
|
||||||
let line = self.byte_to_line(start).unwrap();
|
let line = self.byte_to_line(start).unwrap();
|
||||||
@ -283,8 +283,39 @@ impl SourceFile {
|
|||||||
self.line_starts
|
self.line_starts
|
||||||
.extend(newlines(&self.src[start ..]).map(|idx| start + idx));
|
.extend(newlines(&self.src[start ..]).map(|idx| start + idx));
|
||||||
|
|
||||||
// Reparse.
|
// Update the root node.
|
||||||
self.root = parse(&self.src);
|
#[cfg(not(feature = "parse-cache"))]
|
||||||
|
{
|
||||||
|
self.root = parse(&self.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parse-cache")]
|
||||||
|
{
|
||||||
|
let insertion_span = replace.into_span(self.id);
|
||||||
|
let incremental_target =
|
||||||
|
Rc::make_mut(&mut self.root).incremental_parent(insertion_span);
|
||||||
|
|
||||||
|
match incremental_target {
|
||||||
|
Some((child_idx, parent, offset)) => {
|
||||||
|
let child = &parent.children()[child_idx];
|
||||||
|
let src = &self.src[offset .. offset + child.len()];
|
||||||
|
let parse_res = match child.kind() {
|
||||||
|
NodeKind::Markup => Some(parse(src)),
|
||||||
|
_ => parse_block(src),
|
||||||
|
}
|
||||||
|
.and_then(|x| x.data().erroneous().not().then(|| x));
|
||||||
|
|
||||||
|
if let Some(parse_res) = parse_res {
|
||||||
|
parent.replace_child(child_idx, parse_res);
|
||||||
|
} else {
|
||||||
|
self.root = parse(&self.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.root = parse(&self.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide highlighting categories for the given range of the source file.
|
/// Provide highlighting categories for the given range of the source file.
|
||||||
@ -473,4 +504,21 @@ mod tests {
|
|||||||
// Test removing everything.
|
// Test removing everything.
|
||||||
test(TEST, 0 .. 21, "", "");
|
test(TEST, 0 .. 21, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_source_file_edit_2() {
|
||||||
|
#[track_caller]
|
||||||
|
fn test(prev: &str, range: Range<usize>, with: &str, after: &str) {
|
||||||
|
let mut source = SourceFile::detached(prev);
|
||||||
|
let result = SourceFile::detached(after);
|
||||||
|
dbg!(Green::from(source.root.clone()));
|
||||||
|
source.edit(range, with);
|
||||||
|
assert_eq!(source.src, result.src);
|
||||||
|
assert_eq!(source.line_starts, result.line_starts);
|
||||||
|
dbg!(Green::from(source.root));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test inserting at the begining.
|
||||||
|
test("abc #f()[def] ghi", 10 .. 11, "xyz", "abc #f()[dxyzf] ghi");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,92 @@ impl GreenNode {
|
|||||||
pub fn children(&self) -> &[Green] {
|
pub fn children(&self) -> &[Green] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The node's children, mutably.
|
||||||
|
pub fn children_mut(&mut self) -> &mut [Green] {
|
||||||
|
&mut self.children
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The node's metadata.
|
||||||
|
pub fn data(&self) -> &GreenData {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The node's type.
|
||||||
|
pub fn kind(&self) -> &NodeKind {
|
||||||
|
self.data().kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The node's length.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the parent of the deepest incremental-safe node and the index of
|
||||||
|
/// the found child.
|
||||||
|
pub fn incremental_parent(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
) -> Option<(usize, &mut GreenNode, usize)> {
|
||||||
|
self.incremental_parent_internal(span, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn incremental_parent_internal(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
mut offset: usize,
|
||||||
|
) -> Option<(usize, &mut GreenNode, usize)> {
|
||||||
|
let x = unsafe { &mut *(self as *mut _) };
|
||||||
|
|
||||||
|
for (i, child) in self.children.iter_mut().enumerate() {
|
||||||
|
match child {
|
||||||
|
Green::Token(n) => {
|
||||||
|
if offset < span.start {
|
||||||
|
// the token is strictly before the span
|
||||||
|
offset += n.len();
|
||||||
|
} else {
|
||||||
|
// the token is within or after the span; tokens are
|
||||||
|
// never safe, so we return.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Green::Node(n) => {
|
||||||
|
let end = n.len() + offset;
|
||||||
|
if offset < span.start && end < span.start {
|
||||||
|
// the node is strictly before the span
|
||||||
|
offset += n.len();
|
||||||
|
} else if span.start >= offset
|
||||||
|
&& span.start < end
|
||||||
|
&& span.end <= end
|
||||||
|
&& span.end > offset
|
||||||
|
{
|
||||||
|
// the node is within the span.
|
||||||
|
if n.kind().is_incremental_safe() {
|
||||||
|
let res =
|
||||||
|
Rc::make_mut(n).incremental_parent_internal(span, offset);
|
||||||
|
if res.is_none() {
|
||||||
|
return Some((i, x, offset));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Rc::make_mut(n)
|
||||||
|
.incremental_parent_internal(span, offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the node is overlapping or after after the span; nodes are
|
||||||
|
// never safe, so we return.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace one of the node's children.
|
||||||
|
pub fn replace_child(&mut self, index: usize, child: impl Into<Green>) {
|
||||||
|
self.children[index] = child.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<GreenNode> for Green {
|
impl From<GreenNode> for Green {
|
||||||
@ -653,6 +739,14 @@ impl NodeKind {
|
|||||||
matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_))
|
matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether it is safe to do incremental parsing on this node.
|
||||||
|
pub fn is_incremental_safe(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Block | Self::Markup => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A human-readable name for the kind.
|
/// A human-readable name for the kind.
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
@ -125,6 +125,17 @@ impl Span {
|
|||||||
*self = self.join(other)
|
*self = self.join(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new span with n characters inserted inside of this span.
|
||||||
|
pub fn inserted(mut self, other: Self, n: usize) -> Self {
|
||||||
|
if !self.contains(other.start) || !self.contains(other.end) {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let len_change = (n as isize - other.len() as isize) as usize;
|
||||||
|
self.end += len_change;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Test whether a position is within the span.
|
/// Test whether a position is within the span.
|
||||||
pub fn contains(&self, pos: usize) -> bool {
|
pub fn contains(&self, pos: usize) -> bool {
|
||||||
self.start <= pos && self.end >= pos
|
self.start <= pos && self.end >= pos
|
||||||
|
Loading…
x
Reference in New Issue
Block a user