summaryrefslogtreecommitdiffstats
path: root/src/diff/graph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/diff/graph.rs')
-rw-r--r--src/diff/graph.rs190
1 files changed, 99 insertions, 91 deletions
diff --git a/src/diff/graph.rs b/src/diff/graph.rs
index 7ea7ed38f..9b73c6c43 100644
--- a/src/diff/graph.rs
+++ b/src/diff/graph.rs
@@ -19,7 +19,7 @@ use crate::{
stack::Stack,
},
hash::DftHashMap,
- parse::syntax::{AtomKind, Syntax, SyntaxId},
+ parse::syntax::{AtomKind, Syntax, SyntaxId, SyntaxIdMap},
};
/// A vertex in a directed acyclic graph that represents a diff.
@@ -54,11 +54,9 @@ use crate::{
pub(crate) struct Vertex<'s, 'b> {
pub(crate) neighbours: RefCell<Option<&'b [(Edge, &'b Vertex<'s, 'b>)]>>,
pub(crate) predecessor: Cell<Option<(u32, &'b Vertex<'s, 'b>)>>,
- // TODO: experiment with storing SyntaxId only, and have a HashMap
- // from SyntaxId to &Syntax.
- pub(crate) lhs_syntax: Option<&'s Syntax<'s>>,
- pub(crate) rhs_syntax: Option<&'s Syntax<'s>>,
- parents: Stack<'b, EnteredDelimiter<'s, 'b>>,
+ pub(crate) lhs_syntax: Option<SyntaxId>,
+ pub(crate) rhs_syntax: Option<SyntaxId>,
+ parents: Stack<'b, EnteredDelimiter<'b>>,
lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>,
}
@@ -82,12 +80,12 @@ impl<'s, 'b> PartialEq for Vertex<'s, 'b> {
// more vertices to be distinct, exponentially increasing
// the graph size relative to tree depth.
let b0 = match (self.lhs_syntax, other.lhs_syntax) {
- (Some(s0), Some(s1)) => s0.id() == s1.id(),
+ (Some(s0), Some(s1)) => s0 == s1,
(None, None) => self.lhs_parent_id == other.lhs_parent_id,
_ => false,
};
let b1 = match (self.rhs_syntax, other.rhs_syntax) {
- (Some(s0), Some(s1)) => s0.id() == s1.id(),
+ (Some(s0), Some(s1)) => s0 == s1,
(None, None) => self.rhs_parent_id == other.rhs_parent_id,
_ => false,
};
@@ -105,8 +103,8 @@ impl<'s, 'b> Eq for Vertex<'s, 'b> {}
impl<'s, 'b> Hash for Vertex<'s, 'b> {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.lhs_syntax.map(|node| node.id()).hash(state);
- self.rhs_syntax.map(|node| node.id()).hash(state);
+ self.lhs_syntax.hash(state);
+ self.rhs_syntax.hash(state);
self.lhs_parent_id.hash(state);
self.rhs_parent_id.hash(state);
@@ -116,12 +114,12 @@ impl<'s, 'b> Hash for Vertex<'s, 'b> {
/// Tracks entering syntax List nodes.
#[derive(Clone, PartialEq)]
-enum EnteredDelimiter<'s, 'b> {
+enum EnteredDelimiter<'b> {
/// If we've entered the LHS or RHS separately, we can pop either
/// side independently.
///
/// Assumes that at least one stack is non-empty.
- PopEither((Stack<'b, &'s Syntax<'s>>, Stack<'b, &'s Syntax<'s>>)),
+ PopEither((Stack<'b, SyntaxId>, Stack<'b, SyntaxId>)),
/// If we've entered the LHS and RHS together, we must pop both
/// sides together too. Otherwise we'd consider the following case to have no changes.
///
@@ -129,10 +127,10 @@ enum EnteredDelimiter<'s, 'b> {
/// Old: (a b c)
/// New: (a b) c
/// ```
- PopBoth((&'s Syntax<'s>, &'s Syntax<'s>)),
+ PopBoth((SyntaxId, SyntaxId)),
}
-impl<'s, 'b> fmt::Debug for EnteredDelimiter<'s, 'b> {
+impl<'b> fmt::Debug for EnteredDelimiter<'b> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self {
EnteredDelimiter::PopEither((lhs_delims, rhs_delims)) => {
@@ -148,12 +146,12 @@ impl<'s, 'b> fmt::Debug for EnteredDelimiter<'s, 'b> {
}
}
-fn push_both_delimiters<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
- lhs_delim: &'s Syntax<'s>,
- rhs_delim: &'s Syntax<'s>,
+fn push_both_delimiters<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
+ lhs_delim: SyntaxId,
+ rhs_delim: SyntaxId,
alloc: &'b Bump,
-) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
+) -> Stack<'b, EnteredDelimiter<'b>> {
entered.push(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim)), alloc)
}
@@ -161,25 +159,21 @@ fn can_pop_either_parent(entered: &Stack<EnteredDelimiter>) -> bool {
matches!(entered.peek(), Some(EnteredDelimiter::PopEither(_)))
}
-fn try_pop_both<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
-) -> Option<(
- &'s Syntax<'s>,
- &'s Syntax<'s>,
- Stack<'b, EnteredDelimiter<'s, 'b>>,
-)> {
+fn try_pop_both<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
+) -> Option<(SyntaxId, SyntaxId, Stack<'b, EnteredDelimiter<'b>>)> {
match entered.peek() {
Some(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim))) => {
- Some((lhs_delim, rhs_delim, entered.pop().unwrap()))
+ Some((*lhs_delim, *rhs_delim, entered.pop().unwrap()))
}
_ => None,
}
}
-fn try_pop_lhs<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
+fn try_pop_lhs<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
alloc: &'b Bump,
-) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> {
+) -> Option<(SyntaxId, Stack<'b, EnteredDelimiter<'b>>)> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match lhs_delims.peek() {
Some(lhs_delim) => {
@@ -193,7 +187,7 @@ fn try_pop_lhs<'s, 'b>(
);
}
- Some((lhs_delim, entered))
+ Some((*lhs_delim, entered))
}
None => None,
},
@@ -201,10 +195,10 @@ fn try_pop_lhs<'s, 'b>(
}
}
-fn try_pop_rhs<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
+fn try_pop_rhs<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
alloc: &'b Bump,
-) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> {
+) -> Option<(SyntaxId, Stack<'b, EnteredDelimiter<'b>>)> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match rhs_delims.peek() {
Some(rhs_delim) => {
@@ -218,7 +212,7 @@ fn try_pop_rhs<'s, 'b>(
);
}
- Some((rhs_delim, entered))
+ Some((*rhs_delim, entered))
}
None => None,
},
@@ -226,11 +220,11 @@ fn try_pop_rhs<'s, 'b>(
}
}
-fn push_lhs_delimiter<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
- delimiter: &'s Syntax<'s>,
+fn push_lhs_delimiter<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
+ delimiter: SyntaxId,
alloc: &'b Bump,
-) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
+) -> Stack<'b, EnteredDelimiter<'b>> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.push(delimiter, alloc), rhs_delims.clone())),
@@ -243,11 +237,11 @@ fn push_lhs_delimiter<'s, 'b>(
}
}
-fn push_rhs_delimiter<'s, 'b>(
- entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
- delimiter: &'s Syntax<'s>,
+fn push_rhs_delimiter<'b>(
+ entered: &Stack<'b, EnteredDelimiter<'b>>,
+ delimiter: SyntaxId,
alloc: &'b Bump,
-) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
+) -> Stack<'b, EnteredDelimiter<'b>> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.clone(), rhs_delims.push(delimiter, alloc))),
@@ -265,10 +259,7 @@ impl<'s, 'b> Vertex<'s, 'b> {
self.lhs_syntax.is_none() && self.rhs_syntax.is_none() && self.parents.is_empty()
}
- pub(crate) fn new(
- lhs_syntax: Option<&'s Syntax<'s>>,
- rhs_syntax: Option<&'s Syntax<'s>>,
- ) -> Self {
+ pub(crate) fn new(lhs_syntax: Option<SyntaxId>, rhs_syntax: Option<SyntaxId>) -> Self {
let parents = Stack::new();
Vertex {
neighbours: RefCell::new(None),
@@ -426,59 +417,64 @@ fn looks_like_punctuation(node: &Syntax) -> bool {
/// Pop as many parents of `lhs_node` and `rhs_node` as
/// possible. Return the new syntax nodes and parents.
fn pop_all_parents<'s, 'b>(
- lhs_node: Option<&'s Syntax<'s>>,
- rhs_node: Option<&'s Syntax<'s>>,
+ lhs_node_id: Option<SyntaxId>,
+ rhs_node_id: Option<SyntaxId>,
lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>,
- parents: &Stack<'b, EnteredDelimiter<'s, 'b>>,
+ parents: &Stack<'b, EnteredDelimiter<'b>>,
alloc: &'b Bump,
- id_map: &DftHashMap<SyntaxId, &'s Syntax<'s>>,
+ id_map: &SyntaxIdMap<'s>,
) -> (
- Option<&'s Syntax<'s>>,
- Option<&'s Syntax<'s>>,
Option<SyntaxId>,
Option<SyntaxId>,
- Stack<'b, EnteredDelimiter<'s, 'b>>,
+ Option<SyntaxId>,
+ Option<SyntaxId>,
+ Stack<'b, EnteredDelimiter<'b>>,
) {
- let mut lhs_node = lhs_node;
- let mut rhs_node = rhs_node;
- let mut lhs_parent_id = lhs_parent_id;
- let mut rhs_parent_id = rhs_parent_id;
+ let mut lhs_node_id: Option<SyntaxId> = lhs_node_id;
+ let mut rhs_node_id: Option<SyntaxId> = rhs_node_id;
+ let mut lhs_parent_id: Option<SyntaxId> = lhs_parent_id;
+ let mut rhs_parent_id: Option<SyntaxId> = rhs_parent_id;
let mut parents = parents.clone();
loop {
- if lhs_node.is_none() {
- if let Some((lhs_parent, parents_next)) = try_pop_lhs(&parents, alloc) {
+ if lhs_node_id.is_none() {
+ if let Some((lhs_parent_id_, parents_next)) = try_pop_lhs(&parents, alloc) {
+ let lhs_parent = id_map[&lhs_parent_id_];
// Move to next after LHS parent.
// Continue from sibling of parent.
- lhs_node = lhs_parent.next_sibling();
- lhs_parent_id = lhs_parent.parent().map(Syntax::id);
+ lhs_node_id = lhs_parent.next_sibling().map(|n| n.id());
+ lhs_parent_id = lhs_parent.parent().map(|n| n.id());
parents = parents_next;
continue;
}
}
- if rhs_node.is_none() {
- if let Some((rhs_parent, parents_next)) = try_pop_rhs(&parents, alloc) {
+ if rhs_node_id.is_none() {
+ if let Some((rhs_parent_id_, parents_next)) = try_pop_rhs(&parents, alloc) {
+ let rhs_parent = id_map[&rhs_parent_id_];
// Move to next after RHS parent.
// Continue from sibling of parent.
- rhs_node = rhs_parent.next_sibling();
+ rhs_node_id = rhs_parent.next_sibling().map(|n| n.id());
rhs_parent_id = rhs_parent.parent().map(Syntax::id);
parents = parents_next;
continue;
}
}
- if lhs_node.is_none() && rhs_node.is_none() {
+ if lhs_node_id.is_none() && rhs_node_id.is_none() {
// We have exhausted all the nodes on both lists, so we can
// move up to the parent node.
// Continue from sibling of parent.
- if let Some((lhs_parent, rhs_parent, parents_next)) = try_pop_both(&parents) {
- lhs_node = lhs_parent.next_sibling();
- rhs_node = rhs_parent.next_sibling();
+ if let Some((lhs_parent_id_, rhs_parent_id_, parents_next)) = try_pop_both(&parents) {
+ let lhs_parent = id_map[&lhs_parent_id_];
+ let rhs_parent = id_map[&rhs_parent_id_];
+
+ lhs_node_id = lhs_parent.next_sibling().map(|n| n.id());
+ rhs_node_id = rhs_parent.next_sibling().map(|n| n.id());
lhs_parent_id = lhs_parent.parent().map(Syntax::id);
rhs_parent_id = rhs_parent.parent().map(Syntax::id);
parents = parents_next;
@@ -489,7 +485,13 @@ fn pop_all_parents<'s, 'b>(
break;
}
- (lhs_node, rhs_node, lhs_parent_id, rhs_parent_id, parents)
+ (
+ lhs_node_id,
+ rhs_node_id,
+ lhs_parent_id,
+ rhs_parent_id,
+ parents,
+ )
}
/// Compute the neighbours of `v` if we haven't previously done so,
@@ -498,7 +500,7 @@ pub(crate) fn set_neighbours<'s, 'b>(
v: &Vertex<'s, 'b>,
alloc: &'b Bump,
seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>,
- id_map: &DftHashMap<SyntaxId, &'s Syntax<'s>>,
+ id_map: &SyntaxIdMap<'s>,
) {
if v.neighbours.borrow().is_some() {
return;
@@ -507,7 +509,10 @@ pub(crate) fn set_neighbours<'s, 'b>(
// There are only seven pushes in this function, so that's sufficient.
let mut neighbours: Vec<(Edge, &Vertex)> = Vec::with_capacity(7);
- if let (Some(lhs_syntax), Some(rhs_syntax)) = (&v.lhs_syntax, &v.rhs_syntax) {
+ if let (Some(lhs_syntax_id), Some(rhs_syntax_id)) = (&v.lhs_syntax, &v.rhs_syntax) {
+ let lhs_syntax = id_map[lhs_syntax_id];
+ let rhs_syntax = id_map[rhs_syntax_id];
+
if lhs_syntax == rhs_syntax {
let depth_difference = (lhs_syntax.num_ancestors() as i32
- rhs_syntax.num_ancestors() as i32)
@@ -517,8 +522,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
// Both nodes are equal, the happy case.
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) = pop_all_parents(
- lhs_syntax.next_sibling(),
- rhs_syntax.next_sibling(),
+ lhs_syntax.next_sibling().map(|n| n.id()),
+ rhs_syntax.next_sibling().map(|n| n.id()),
v.lhs_parent_id,
v.rhs_parent_id,
&v.parents,
@@ -568,7 +573,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
let rhs_next = rhs_children.first().copied();
// TODO: be consistent between parents_next and next_parents.
- let parents_next = push_both_delimiters(&v.parents, lhs_syntax, rhs_syntax, alloc);
+ let parents_next =
+ push_both_delimiters(&v.parents, lhs_syntax.id(), rhs_syntax.id(), alloc);
let depth_difference = (lhs_syntax.num_ancestors() as i32
- rhs_syntax.num_ancestors() as i32)
@@ -578,8 +584,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
// pop several levels if the list has no children.
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
- lhs_next,
- rhs_next,
+ lhs_next.map(|n| n.id()),
+ rhs_next.map(|n| n.id()),
Some(lhs_syntax.id()),
Some(rhs_syntax.id()),
&parents_next,
@@ -632,8 +638,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
- lhs_syntax.next_sibling(),
- rhs_syntax.next_sibling(),
+ lhs_syntax.next_sibling().map(|n| n.id()),
+ rhs_syntax.next_sibling().map(|n| n.id()),
v.lhs_parent_id,
v.rhs_parent_id,
&v.parents,
@@ -660,13 +666,14 @@ pub(crate) fn set_neighbours<'s, 'b>(
}
}
- if let Some(lhs_syntax) = &v.lhs_syntax {
+ if let Some(lhs_syntax_id) = &v.lhs_syntax {
+ let lhs_syntax = id_map[lhs_syntax_id];
match lhs_syntax {
// Step over this novel atom.
Syntax::Atom { .. } => {
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
- lhs_syntax.next_sibling(),
+ lhs_syntax.next_sibling().map(|n| n.id()),
v.rhs_syntax,
v.lhs_parent_id,
v.rhs_parent_id,
@@ -696,11 +703,11 @@ pub(crate) fn set_neighbours<'s, 'b>(
Syntax::List { children, .. } => {
let lhs_next = children.first().copied();
- let parents_next = push_lhs_delimiter(&v.parents, lhs_syntax, alloc);
+ let parents_next = push_lhs_delimiter(&v.parents, lhs_syntax.id(), alloc);
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
- lhs_next,
+ lhs_next.map(|n| n.id()),
v.rhs_syntax,
Some(lhs_syntax.id()),
v.rhs_parent_id,
@@ -729,14 +736,15 @@ pub(crate) fn set_neighbours<'s, 'b>(
}
}
- if let Some(rhs_syntax) = &v.rhs_syntax {
+ if let Some(rhs_syntax_id) = &v.rhs_syntax {
+ let rhs_syntax = id_map[rhs_syntax_id];
match rhs_syntax {
// Step over this novel atom.
Syntax::Atom { .. } => {
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
v.lhs_syntax,
- rhs_syntax.next_sibling(),
+ rhs_syntax.next_sibling().map(|n| n.id()),
v.lhs_parent_id,
v.rhs_parent_id,
&v.parents,
@@ -764,12 +772,12 @@ pub(crate) fn set_neighbours<'s, 'b>(
// Step into this partially/fully novel list.
Syntax::List { children, .. } => {
let rhs_next = children.first().copied();
- let parents_next = push_rhs_delimiter(&v.parents, rhs_syntax, alloc);
+ let parents_next = push_rhs_delimiter(&v.parents, rhs_syntax.id(), alloc);
let (lhs_syntax, rhs_syntax, lhs_parent_id, rhs_parent_id, parents) =
pop_all_parents(
v.lhs_syntax,
- rhs_next,
+ rhs_next.map(|n| n.id()),
v.lhs_parent_id,
Some(rhs_syntax.id()),
&parents_next,
@@ -807,8 +815,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
pub(crate) fn populate_change_map<'s, 'b>(
route: &[(Edge, &'b Vertex<'s, 'b>)],
- change_map: &mut ChangeMap<'s>,
- id_map: &DftHashMap<SyntaxId, &'s Syntax<'s>>,
+ change_map: &mut ChangeMap,
+ id_map: &SyntaxIdMap<'s>,
) {
for (e, v) in route {
match e {
@@ -817,8 +825,8 @@ pub(crate) fn populate_change_map<'s, 'b>(
let lhs = v.lhs_syntax.unwrap();
let rhs = v.rhs_syntax.unwrap();
- insert_deep_unchanged(lhs, rhs, change_map);
- insert_deep_unchanged(rhs, lhs, change_map);
+ insert_deep_unchanged(lhs, rhs, id_map, change_map);
+ insert_deep_unchanged(rhs, lhs, id_map, change_map);
}
EnterUnchangedDelimiter { .. } => {
// No change on the outer delimiter, but children may