Turn reparsing methods into free functions

This commit is contained in:
Laurenz 2022-06-14 14:03:13 +02:00
parent c81e2a5f56
commit e03f32ca34

View File

@ -20,8 +20,8 @@ pub fn reparse(
replacement_len: usize, replacement_len: usize,
) -> Range<usize> { ) -> Range<usize> {
if let SyntaxNode::Inner(inner) = root { if let SyntaxNode::Inner(inner) = root {
let reparser = Reparser { src, replaced, replacement_len }; let change = Change { src, replaced, replacement_len };
if let Some(range) = reparser.reparse_step(Arc::make_mut(inner), 0, true, true) { if let Some(range) = try_reparse(&change, Arc::make_mut(inner), 0, true, true) {
return range; return range;
} }
} }
@ -32,28 +32,14 @@ pub fn reparse(
0 .. src.len() 0 .. src.len()
} }
/// Allows partial refreshs of the syntax tree. /// Try to reparse inside the given node.
/// fn try_reparse(
/// This struct holds a description of a change. Its methods can be used to try change: &Change,
/// and apply the change to a syntax tree.
struct Reparser<'a> {
/// The new source code, with the change applied.
src: &'a str,
/// Which range in the old source file was changed.
replaced: Range<usize>,
/// How many characters replaced the text in `replaced`.
replacement_len: usize,
}
impl Reparser<'_> {
/// Try to reparse inside the given node.
fn reparse_step(
&self,
node: &mut InnerNode, node: &mut InnerNode,
mut offset: usize, mut offset: usize,
outermost: bool, outermost: bool,
safe_to_replace: bool, safe_to_replace: bool,
) -> Option<Range<usize>> { ) -> Option<Range<usize>> {
let is_markup = matches!(node.kind(), NodeKind::Markup { .. }); let is_markup = matches!(node.kind(), NodeKind::Markup { .. });
let original_count = node.children().len(); let original_count = node.children().len();
let original_offset = offset; let original_offset = offset;
@ -76,20 +62,20 @@ impl Reparser<'_> {
match search { match search {
SearchState::NoneFound => { SearchState::NoneFound => {
// The edit is contained within the span of the current element. // The edit is contained within the span of the current element.
if child_span.contains(&self.replaced.start) if child_span.contains(&change.replaced.start)
&& child_span.end >= self.replaced.end && child_span.end >= change.replaced.end
{ {
// In Markup mode, we want to consider a non-whitespace // In Markup mode, we want to consider a non-whitespace
// neighbor if the edit is on the node boundary. // neighbor if the edit is on the node boundary.
search = if is_markup && child_span.end == self.replaced.end { search = if is_markup && child_span.end == change.replaced.end {
SearchState::RequireNonTrivia(pos) SearchState::RequireNonTrivia(pos)
} else { } else {
SearchState::Contained(pos) SearchState::Contained(pos)
}; };
} else if child_span.contains(&self.replaced.start) { } else if child_span.contains(&change.replaced.start) {
search = SearchState::Inside(pos); search = SearchState::Inside(pos);
} else if child_span.end == self.replaced.start } else if child_span.end == change.replaced.start
&& self.replaced.start == self.replaced.end && change.replaced.start == change.replaced.end
&& child_outermost && child_outermost
{ {
search = SearchState::SpanFound(pos, pos); search = SearchState::SpanFound(pos, pos);
@ -107,23 +93,20 @@ impl Reparser<'_> {
if !child.kind().is_space() if !child.kind().is_space()
&& child.kind() != &NodeKind::Semicolon && child.kind() != &NodeKind::Semicolon
&& child.kind() != &NodeKind::Text('/'.into()) && child.kind() != &NodeKind::Text('/'.into())
&& (ahead.is_none() || self.replaced.start > child_span.end) && (ahead.is_none() || change.replaced.start > child_span.end)
&& !ahead.map_or(false, Ahead::is_compulsory) && !ahead.map_or(false, Ahead::is_compulsory)
{ {
ahead = Some(Ahead::new( ahead =
pos, Some(Ahead::new(pos, at_start, child.kind().is_bounded()));
at_start,
child.kind().is_bounded(),
));
} }
at_start = child.kind().is_at_start(at_start); at_start = child.kind().is_at_start(at_start);
} }
} }
SearchState::Inside(start) => { SearchState::Inside(start) => {
if child_span.end == self.replaced.end { if child_span.end == change.replaced.end {
search = SearchState::RequireNonTrivia(start); search = SearchState::RequireNonTrivia(start);
} else if child_span.end > self.replaced.end { } else if child_span.end > change.replaced.end {
search = SearchState::SpanFound(start, pos); search = SearchState::SpanFound(start, pos);
} }
} }
@ -160,7 +143,8 @@ impl Reparser<'_> {
let prev_descendants = child.descendants(); let prev_descendants = child.descendants();
if let Some(range) = match child { if let Some(range) = match child {
SyntaxNode::Inner(node) => self.reparse_step( SyntaxNode::Inner(node) => try_reparse(
change,
Arc::make_mut(node), Arc::make_mut(node),
pos.offset, pos.offset,
child_outermost, child_outermost,
@ -184,7 +168,8 @@ impl Reparser<'_> {
// Return if the element was reparsable on its own, otherwise try to // Return if the element was reparsable on its own, otherwise try to
// treat it as a markup element. // treat it as a markup element.
if let Some(func) = func { if let Some(func) = func {
if let Some(result) = self.replace( if let Some(result) = replace(
change,
node, node,
func, func,
pos.idx .. pos.idx + 1, pos.idx .. pos.idx + 1,
@ -205,7 +190,7 @@ impl Reparser<'_> {
let (mut start, end) = search.done()?; let (mut start, end) = search.done()?;
if let Some(ahead) = ahead { if let Some(ahead) = ahead {
if start.offset == self.replaced.start || ahead.is_compulsory() { if start.offset == change.replaced.start || ahead.is_compulsory() {
start = ahead.pos; start = ahead.pos;
at_start = ahead.at_start; at_start = ahead.at_start;
} }
@ -216,60 +201,60 @@ impl Reparser<'_> {
let superseded_span = let superseded_span =
start.offset .. end.offset + node.children().as_slice()[end.idx].len(); start.offset .. end.offset + node.children().as_slice()[end.idx].len();
self.replace( replace(
change,
node, node,
ReparseMode::MarkupElements { at_start, min_indent }, ReparseMode::MarkupElements { at_start, min_indent },
start.idx .. end.idx + 1, start.idx .. end.idx + 1,
superseded_span, superseded_span,
outermost, outermost,
) )
} }
fn replace( /// Reparse the superseded nodes and replace them.
&self, fn replace(
change: &Change,
node: &mut InnerNode, node: &mut InnerNode,
mode: ReparseMode, mode: ReparseMode,
superseded_idx: Range<usize>, superseded_idx: Range<usize>,
superseded_span: Range<usize>, superseded_span: Range<usize>,
outermost: bool, outermost: bool,
) -> Option<Range<usize>> { ) -> Option<Range<usize>> {
let superseded_start = superseded_idx.start; let superseded_start = superseded_idx.start;
let differential: isize = let differential: isize =
self.replacement_len as isize - self.replaced.len() as isize; change.replacement_len as isize - change.replaced.len() as isize;
let newborn_end = (superseded_span.end as isize + differential) as usize; let newborn_end = (superseded_span.end as isize + differential) as usize;
let newborn_span = superseded_span.start .. newborn_end; let newborn_span = superseded_span.start .. newborn_end;
let mut prefix = ""; let mut prefix = "";
for (i, c) in self.src[.. newborn_span.start].char_indices().rev() { for (i, c) in change.src[.. newborn_span.start].char_indices().rev() {
if is_newline(c) { if is_newline(c) {
break; break;
} }
prefix = &self.src[i .. newborn_span.start]; prefix = &change.src[i .. newborn_span.start];
} }
let (newborns, terminated, amount) = match mode { let (newborns, terminated, amount) = match mode {
ReparseMode::Code => reparse_code_block( ReparseMode::Code => reparse_code_block(
&prefix, &prefix,
&self.src[newborn_span.start ..], &change.src[newborn_span.start ..],
newborn_span.len(), newborn_span.len(),
), ),
ReparseMode::Content => reparse_content_block( ReparseMode::Content => reparse_content_block(
&prefix, &prefix,
&self.src[newborn_span.start ..], &change.src[newborn_span.start ..],
newborn_span.len(), newborn_span.len(),
), ),
ReparseMode::MarkupElements { at_start, min_indent } => { ReparseMode::MarkupElements { at_start, min_indent } => reparse_markup_elements(
reparse_markup_elements(
&prefix, &prefix,
&self.src[newborn_span.start ..], &change.src[newborn_span.start ..],
newborn_span.len(), newborn_span.len(),
differential, differential,
&node.children().as_slice()[superseded_start ..], &node.children().as_slice()[superseded_start ..],
at_start, at_start,
min_indent, min_indent,
) ),
}
}?; }?;
// Do not accept unclosed nodes if the old node wasn't at the right edge // Do not accept unclosed nodes if the old node wasn't at the right edge
@ -282,16 +267,16 @@ impl Reparser<'_> {
.ok()?; .ok()?;
Some(newborn_span) Some(newborn_span)
}
} }
/// The position of a syntax node. /// A description of a change.
#[derive(Clone, Copy, Debug, PartialEq)] struct Change<'a> {
struct NodePos { /// The new source code, with the change applied.
/// The index in the parent node. src: &'a str,
idx: usize, /// Which range in the old source file was changed.
/// The byte offset in the string. replaced: Range<usize>,
offset: usize, /// How many characters replaced the text in `replaced`.
replacement_len: usize,
} }
/// Encodes the state machine of the search for the nodes are pending for /// Encodes the state machine of the search for the nodes are pending for
@ -332,6 +317,15 @@ impl SearchState {
} }
} }
/// The position of a syntax node.
#[derive(Clone, Copy, Debug, PartialEq)]
struct NodePos {
/// The index in the parent node.
idx: usize,
/// The byte offset in the string.
offset: usize,
}
/// An ahead node with an index and whether it is `at_start`. /// An ahead node with an index and whether it is `at_start`.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
struct Ahead { struct Ahead {