Handle nested sliders correctly when preferring the outer delimiter

Previously we didn't check the state of children, which was an
oversight from the original implementation. As a result, we fixed
nested sliders in fewer situations.

Fixes #535
text_sliders
Wilfred Hughes 2023-07-14 08:45:12 +07:00
parent a5d3cb55b7
commit 7caaaf7fcf
5 changed files with 80 additions and 21 deletions

@ -4,6 +4,11 @@
Improved parsing of qualified constructors in Haskell. Improved parsing of qualified constructors in Haskell.
### Diffing
Improved handling of delimiters ("nested sliders") in language that
prefer the outer delimiter, such as JSON and Lisps.
## 0.48 (released 12th July 2023) ## 0.48 (released 12th July 2023)
### Parsing ### Parsing

@ -136,6 +136,9 @@ sample_files/multiline_string_before.ml sample_files/multiline_string_after.ml
sample_files/nest_before.rs sample_files/nest_after.rs sample_files/nest_before.rs sample_files/nest_after.rs
59f4e15e83e05c0fa36d03f4b2bb4cf4 - 59f4e15e83e05c0fa36d03f4b2bb4cf4 -
sample_files/nested_slider_before.el sample_files/nested_slider_after.el
dc30db682f9f17dd2866a7b5f2a9a081 -
sample_files/nested_slider_before.rs sample_files/nested_slider_after.rs sample_files/nested_slider_before.rs sample_files/nested_slider_after.rs
5c3dc3d870cdf182658da6a29650d911 - 5c3dc3d870cdf182658da6a29650d911 -

@ -0,0 +1,8 @@
(defun deadgrep--project-root ()
"Guess the project root of the given FILE-PATH."
(let ((root default-directory)
(project (project-current)))
(when project
(setq root (project-root project)))
(when root
(deadgrep--lookup-override root))))

@ -0,0 +1,10 @@
(defun deadgrep--project-root ()
"Guess the project root of the given FILE-PATH."
(let ((root default-directory)
(project (project-current)))
(when project
(-when-let (roots (project-roots project))
(setq root (car roots))))
(when root
(deadgrep--lookup-override root))))

@ -122,20 +122,21 @@ fn fix_nested_slider_prefer_outer<'a>(node: &'a Syntax<'a>, change_map: &mut Cha
.expect("Changes should be set before slider correction") .expect("Changes should be set before slider correction")
{ {
Unchanged(_) => { Unchanged(_) => {
// All children should be novel except one descendant. let mut candidates = vec![];
let mut found_unchanged = vec![]; unchanged_descendants_for_outer_slider(children, &mut candidates, change_map);
unchanged_descendants_ignore_delim(children, &mut found_unchanged, change_map);
// We can slide if there is a single unchanged
if let [unchanged] = found_unchanged[..] { // descendant, that descendant is a list, and that
if matches!(unchanged, List { .. }) // list has novel delimiters.
&& matches!(change_map.get(unchanged), Some(Novel)) if let [candidate] = candidates[..] {
if matches!(candidate, List { .. })
&& matches!(change_map.get(candidate), Some(Novel))
{ {
push_unchanged_to_descendant(node, unchanged, change_map); push_unchanged_to_descendant(node, candidate, change_map);
} }
} }
} }
ReplacedComment(_, _) | ReplacedString(_, _) => {} ReplacedComment(_, _) | ReplacedString(_, _) | Novel => {}
Novel => {}
} }
for child in children { for child in children {
@ -197,9 +198,18 @@ fn unchanged_descendants<'a>(
} }
} }
/// Find the descendants of `nodes` that are unchanged, but ignore the /// Nested sliders require a single unchanged descendant whose
/// delimiter on list nodes. /// delimiters we can slide.
fn unchanged_descendants_ignore_delim<'a>( ///
/// ```
/// (old-1 (novel (old-2)))
/// ```
///
/// To slide, we want a single list that contains unchanged items but
/// the outer delimiters are novel.
///
/// Find all the unchanged descendants.
fn unchanged_descendants_for_outer_slider<'a>(
nodes: &[&'a Syntax<'a>], nodes: &[&'a Syntax<'a>],
found: &mut Vec<&'a Syntax<'a>>, found: &mut Vec<&'a Syntax<'a>>,
change_map: &ChangeMap<'a>, change_map: &ChangeMap<'a>,
@ -216,24 +226,47 @@ fn unchanged_descendants_ignore_delim<'a>(
match node { match node {
Atom { .. } => { Atom { .. } => {
if is_unchanged { if is_unchanged {
// If there's an unchanged atom descendant, we
// can't slide. Sliding the delimiters requires a
// single list, or we are potentially changing the
// diff semantically.
//
// Add to the found items, but terminate early
// since we'll never slide.
found.push(node); found.push(node);
break;
} else { } else {
// No problem // Novel atom. This is fine, we're looking for a
// single unchanged node.
} }
} }
List { children, .. } => { List { children, .. } => {
let all_children_unchanged = true;
if is_unchanged { if is_unchanged {
// Outer list is unchanged, not what we wanted. // This list is unchanged, and the delimiters are
// unchanged. It's an unchanged descendant, but we
// won't be able to slide its delimiters because
// its delimiters are unchanged.
//
// Add to the found items, but terminate early
// since we'll never slide.
found.push(node); found.push(node);
break;
} else { } else {
// Is changed. // A list whose outer delimiters are novel.
if all_children_unchanged {
// What we're looking for. let has_unchanged_children = children
.iter()
.any(|node| matches!(change_map.get(node), Some(Unchanged(_))));
if has_unchanged_children {
// The list has unchanged children and novel
// delimiters. This is a candidate for
// sliding.
found.push(node); found.push(node);
} else { } else {
unchanged_descendants_ignore_delim(children, found, change_map); // All of the immediate children are novel,
// recurse in case they have descendants that
// are unchanged.
unchanged_descendants_for_outer_slider(children, found, change_map);
} }
} }
} }