Track whether nodes had a previous novel line

pull/25/head
Wilfred Hughes 2021-07-04 23:49:31 +07:00
parent fbb2dffd4a
commit ba645cfe2a
2 changed files with 94 additions and 12 deletions

@ -2,6 +2,7 @@ use std::cmp::{min, Ordering, Reverse};
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use crate::lines::LineNumber;
use crate::syntax::{ChangeKind, Syntax}; use crate::syntax::{ChangeKind, Syntax};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use Edge::*; use Edge::*;
@ -9,7 +10,9 @@ use Edge::*;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Vertex<'a> { struct Vertex<'a> {
lhs_syntax: Option<&'a Syntax<'a>>, lhs_syntax: Option<&'a Syntax<'a>>,
lhs_prev_novel: Option<LineNumber>,
rhs_syntax: Option<&'a Syntax<'a>>, rhs_syntax: Option<&'a Syntax<'a>>,
rhs_prev_novel: Option<LineNumber>,
} }
impl<'a> PartialEq for Vertex<'a> { impl<'a> PartialEq for Vertex<'a> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
@ -66,8 +69,6 @@ impl<'a> Eq for OrdVertex<'a> {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
enum Edge { enum Edge {
// TODO: Prefer nodes at the same or at least close levels of
// nesting.
UnchangedNode(u64), UnchangedNode(u64),
UnchangedDelimiter(u64), UnchangedDelimiter(u64),
NovelAtomLHS, NovelAtomLHS,
@ -106,10 +107,12 @@ fn shortest_path(start: Vertex) -> Vec<(Edge, Vertex)> {
let mut visited = FxHashSet::default(); let mut visited = FxHashSet::default();
let mut predecessors: FxHashMap<Vertex, (u64, Edge, Vertex)> = FxHashMap::default(); let mut predecessors: FxHashMap<Vertex, (u64, Edge, Vertex)> = FxHashMap::default();
let end;
loop { loop {
match heap.pop() { match heap.pop() {
Some(Reverse(OrdVertex { distance, v })) => { Some(Reverse(OrdVertex { distance, v })) => {
if v.is_end() { if v.is_end() {
end = v;
break; break;
} }
@ -144,10 +147,7 @@ fn shortest_path(start: Vertex) -> Vec<(Edge, Vertex)> {
} }
} }
let mut current = Vertex { let mut current = end;
lhs_syntax: None,
rhs_syntax: None,
};
let mut res: Vec<(Edge, Vertex)> = vec![]; let mut res: Vec<(Edge, Vertex)> = vec![];
loop { loop {
match predecessors.remove(&current) { match predecessors.remove(&current) {
@ -180,6 +180,8 @@ fn neighbours<'a>(v: &Vertex<'a>) -> Vec<(Edge, Vertex<'a>)> {
Vertex { Vertex {
lhs_syntax: lhs_syntax.next(), lhs_syntax: lhs_syntax.next(),
rhs_syntax: rhs_syntax.next(), rhs_syntax: rhs_syntax.next(),
lhs_prev_novel: None,
rhs_prev_novel: None,
}, },
)); ));
} }
@ -220,7 +222,9 @@ fn neighbours<'a>(v: &Vertex<'a>) -> Vec<(Edge, Vertex<'a>)> {
UnchangedDelimiter(depth_difference), UnchangedDelimiter(depth_difference),
Vertex { Vertex {
lhs_syntax: lhs_next, lhs_syntax: lhs_next,
lhs_prev_novel: None,
rhs_syntax: rhs_next, rhs_syntax: rhs_next,
rhs_prev_novel: None,
}, },
)); ));
} }
@ -235,23 +239,28 @@ fn neighbours<'a>(v: &Vertex<'a>) -> Vec<(Edge, Vertex<'a>)> {
NovelAtomLHS, NovelAtomLHS,
Vertex { Vertex {
lhs_syntax: lhs_syntax.next(), lhs_syntax: lhs_syntax.next(),
lhs_prev_novel: lhs_syntax.last_line(),
rhs_syntax: v.rhs_syntax, rhs_syntax: v.rhs_syntax,
rhs_prev_novel: v.rhs_prev_novel,
}, },
)); ));
} }
// Step into this partially/fully novel list. // Step into this partially/fully novel list.
Syntax::List { children, .. } => { Syntax::List { children, .. } => {
let lhs_next = if children.is_empty() { let (lhs_next, lhs_prev_novel) = if children.is_empty() {
lhs_syntax.next() (lhs_syntax.next(), v.lhs_prev_novel)
} else { } else {
Some(children[0]) // `lhs_prev_novel` only tracks nodes at the same level.
(Some(children[0]), None)
}; };
res.push(( res.push((
NovelDelimiterLHS, NovelDelimiterLHS,
Vertex { Vertex {
lhs_syntax: lhs_next, lhs_syntax: lhs_next,
lhs_prev_novel,
rhs_syntax: v.rhs_syntax, rhs_syntax: v.rhs_syntax,
rhs_prev_novel: v.rhs_prev_novel,
}, },
)); ));
} }
@ -266,23 +275,28 @@ fn neighbours<'a>(v: &Vertex<'a>) -> Vec<(Edge, Vertex<'a>)> {
NovelAtomRHS, NovelAtomRHS,
Vertex { Vertex {
lhs_syntax: v.lhs_syntax, lhs_syntax: v.lhs_syntax,
lhs_prev_novel: v.lhs_prev_novel,
rhs_syntax: rhs_syntax.next(), rhs_syntax: rhs_syntax.next(),
rhs_prev_novel: rhs_syntax.last_line(),
}, },
)); ));
} }
// Step into this partially/fully novel list. // Step into this partially/fully novel list.
Syntax::List { children, .. } => { Syntax::List { children, .. } => {
let rhs_next = if children.is_empty() { let (rhs_next, rhs_prev_novel) = if children.is_empty() {
rhs_syntax.next() (rhs_syntax.next(), v.rhs_prev_novel)
} else { } else {
Some(children[0]) // `rhs_prev_novel` only tracks nodes at the same level.
(Some(children[0]), None)
}; };
res.push(( res.push((
NovelDelimiterRHS, NovelDelimiterRHS,
Vertex { Vertex {
lhs_syntax: v.lhs_syntax, lhs_syntax: v.lhs_syntax,
lhs_prev_novel: v.lhs_prev_novel,
rhs_syntax: rhs_next, rhs_syntax: rhs_next,
rhs_prev_novel,
}, },
)); ));
} }
@ -295,7 +309,9 @@ fn neighbours<'a>(v: &Vertex<'a>) -> Vec<(Edge, Vertex<'a>)> {
pub fn mark_syntax<'a>(lhs_syntax: Option<&'a Syntax<'a>>, rhs_syntax: Option<&'a Syntax<'a>>) { pub fn mark_syntax<'a>(lhs_syntax: Option<&'a Syntax<'a>>, rhs_syntax: Option<&'a Syntax<'a>>) {
let start = Vertex { let start = Vertex {
lhs_syntax, lhs_syntax,
lhs_prev_novel: None,
rhs_syntax, rhs_syntax,
rhs_prev_novel: None,
}; };
let route = shortest_path(start); let route = shortest_path(start);
mark_route(&route); mark_route(&route);
@ -351,6 +367,14 @@ mod tests {
}] }]
} }
fn col_helper(line: usize, col: usize) -> Vec<SingleLineSpan> {
vec![SingleLineSpan {
line: line.into(),
start_col: col,
end_col: col + 1,
}]
}
#[test] #[test]
fn identical_atoms() { fn identical_atoms() {
let arena = Arena::new(); let arena = Arena::new();
@ -384,7 +408,9 @@ mod tests {
let start = Vertex { let start = Vertex {
lhs_syntax: Some(lhs), lhs_syntax: Some(lhs),
lhs_prev_novel: None,
rhs_syntax: Some(rhs), rhs_syntax: Some(rhs),
rhs_prev_novel: None,
}; };
let route = shortest_path(start); let route = shortest_path(start);
@ -418,7 +444,9 @@ mod tests {
let start = Vertex { let start = Vertex {
lhs_syntax: lhs.get(0).map(|n| *n), lhs_syntax: lhs.get(0).map(|n| *n),
lhs_prev_novel: None,
rhs_syntax: rhs.get(0).map(|n| *n), rhs_syntax: rhs.get(0).map(|n| *n),
rhs_prev_novel: None,
}; };
let route = shortest_path(start); let route = shortest_path(start);
@ -455,7 +483,9 @@ mod tests {
let start = Vertex { let start = Vertex {
lhs_syntax: lhs.get(0).map(|n| *n), lhs_syntax: lhs.get(0).map(|n| *n),
lhs_prev_novel: None,
rhs_syntax: rhs.get(0).map(|n| *n), rhs_syntax: rhs.get(0).map(|n| *n),
rhs_prev_novel: None,
}; };
let route = shortest_path(start); let route = shortest_path(start);
@ -512,7 +542,9 @@ mod tests {
let start = Vertex { let start = Vertex {
lhs_syntax: lhs.get(0).map(|n| *n), lhs_syntax: lhs.get(0).map(|n| *n),
lhs_prev_novel: None,
rhs_syntax: rhs.get(0).map(|n| *n), rhs_syntax: rhs.get(0).map(|n| *n),
rhs_prev_novel: None,
}; };
let route = shortest_path(start); let route = shortest_path(start);
@ -527,4 +559,29 @@ mod tests {
], ],
); );
} }
#[test]
fn prefer_atoms_same_line() {
let arena = Arena::new();
let lhs: Vec<&Syntax> = vec![
Syntax::new_atom(&arena, col_helper(1, 0), "foo"),
Syntax::new_atom(&arena, col_helper(2, 0), "bar"),
Syntax::new_atom(&arena, col_helper(2, 1), "foo"),
];
init_info(&lhs);
let rhs: Vec<&Syntax> = vec![Syntax::new_atom(&arena, col_helper(1, 0), "foo")];
init_info(&rhs);
let start = Vertex {
lhs_syntax: lhs.get(0).map(|n| *n),
lhs_prev_novel: None,
rhs_syntax: rhs.get(0).map(|n| *n),
rhs_prev_novel: None,
};
let route = shortest_path(start);
let actions = route.iter().map(|(action, _)| *action).collect_vec();
assert_eq!(actions, vec![UnchangedNode(0), NovelAtomLHS, NovelAtomLHS]);
}
} }

@ -217,6 +217,31 @@ impl<'a> Syntax<'a> {
self.info().id.get() self.info().id.get()
} }
pub fn last_line(&self) -> Option<LineNumber> {
let position = match self {
List { close_position, .. } => close_position,
Atom { position, .. } => position,
};
position.last().map(|lp| lp.line)
}
pub fn next_on_same_line(&self) -> bool {
match self.next() {
Some(next) => {
let self_last_line = match self {
List { close_position, .. } => close_position.last().map(|lp| lp.line),
Atom { position, .. } => position.last().map(|lp| lp.line),
};
let next_first_line = match next {
List { open_position, .. } => open_position.first().map(|lp| lp.line),
Atom { position, .. } => position.first().map(|lp| lp.line),
};
self_last_line == next_first_line
}
None => false,
}
}
pub fn set_change(&self, ck: ChangeKind<'a>) { pub fn set_change(&self, ck: ChangeKind<'a>) {
self.info().change.set(Some(ck)); self.info().change.set(Some(ck));
} }