diff options
Diffstat (limited to 'crates/syntax/src')
-rw-r--r-- | crates/syntax/src/algo.rs | 125 |
1 files changed, 89 insertions, 36 deletions
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 9dc7182bd..7ac6076a4 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -111,18 +111,28 @@ pub enum InsertPosition<T> { | |||
111 | 111 | ||
112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; | 112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; |
113 | 113 | ||
114 | #[derive(Debug, Hash, PartialEq, Eq)] | ||
115 | enum TreeDiffInsertPos { | ||
116 | After(SyntaxElement), | ||
117 | AsFirstChild(SyntaxElement), | ||
118 | } | ||
119 | |||
114 | #[derive(Debug)] | 120 | #[derive(Debug)] |
115 | pub struct TreeDiff { | 121 | pub struct TreeDiff { |
116 | replacements: FxHashMap<SyntaxElement, SyntaxElement>, | 122 | replacements: FxHashMap<SyntaxElement, SyntaxElement>, |
117 | deletions: Vec<SyntaxElement>, | 123 | deletions: Vec<SyntaxElement>, |
118 | // the vec as well as the indexmap are both here to preserve order | 124 | // the vec as well as the indexmap are both here to preserve order |
119 | insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>, | 125 | insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>, |
120 | } | 126 | } |
121 | 127 | ||
122 | impl TreeDiff { | 128 | impl TreeDiff { |
123 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { | 129 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { |
124 | for (anchor, to) in self.insertions.iter() { | 130 | for (anchor, to) in self.insertions.iter() { |
125 | to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string())); | 131 | let offset = match anchor { |
132 | TreeDiffInsertPos::After(it) => it.text_range().end(), | ||
133 | TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(), | ||
134 | }; | ||
135 | to.iter().for_each(|to| builder.insert(offset, to.to_string())); | ||
126 | } | 136 | } |
127 | for (from, to) in self.replacements.iter() { | 137 | for (from, to) in self.replacements.iter() { |
128 | builder.replace(from.text_range(), to.to_string()) | 138 | builder.replace(from.text_range(), to.to_string()) |
@@ -188,19 +198,20 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
188 | let lhs_child = lhs_children.next(); | 198 | let lhs_child = lhs_children.next(); |
189 | match (lhs_child.clone(), rhs_children.next()) { | 199 | match (lhs_child.clone(), rhs_children.next()) { |
190 | (None, None) => break, | 200 | (None, None) => break, |
191 | (None, Some(element)) => match last_lhs.clone() { | 201 | (None, Some(element)) => { |
192 | Some(prev) => { | 202 | let insert_pos = match last_lhs.clone() { |
193 | mark::hit!(diff_insert); | 203 | Some(prev) => { |
194 | diff.insertions.entry(prev).or_insert_with(Vec::new).push(element); | 204 | mark::hit!(diff_insert); |
195 | } | 205 | TreeDiffInsertPos::After(prev) |
196 | // first iteration, this means we got no anchor element to insert after | 206 | } |
197 | // therefor replace the parent node instead | 207 | // first iteration, insert into out parent as the first child |
198 | None => { | 208 | None => { |
199 | mark::hit!(diff_replace_parent); | 209 | mark::hit!(diff_insert_as_first_child); |
200 | diff.replacements.insert(lhs.clone().into(), rhs.clone().into()); | 210 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
201 | break; | 211 | } |
202 | } | 212 | }; |
203 | }, | 213 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); |
214 | } | ||
204 | (Some(element), None) => { | 215 | (Some(element), None) => { |
205 | mark::hit!(diff_delete); | 216 | mark::hit!(diff_delete); |
206 | diff.deletions.push(element); | 217 | diff.deletions.push(element); |
@@ -224,8 +235,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
224 | } | 235 | } |
225 | } | 236 | } |
226 | let drain = look_ahead_scratch.drain(..); | 237 | let drain = look_ahead_scratch.drain(..); |
227 | if let Some(prev) = last_lhs.clone().filter(|_| insert) { | 238 | if insert { |
228 | diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain); | 239 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { |
240 | TreeDiffInsertPos::After(prev) | ||
241 | } else { | ||
242 | mark::hit!(insert_first_child); | ||
243 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | ||
244 | }; | ||
245 | |||
246 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain); | ||
229 | rhs_children = rhs_children_clone; | 247 | rhs_children = rhs_children_clone; |
230 | } else { | 248 | } else { |
231 | go(diff, lhs_ele, rhs_ele) | 249 | go(diff, lhs_ele, rhs_ele) |
@@ -629,18 +647,19 @@ mod tests { | |||
629 | 647 | ||
630 | #[test] | 648 | #[test] |
631 | fn replace_parent() { | 649 | fn replace_parent() { |
632 | mark::check!(diff_replace_parent); | 650 | mark::check!(diff_insert_as_first_child); |
633 | check_diff( | 651 | check_diff( |
634 | r#""#, | 652 | r#""#, |
635 | r#"use foo::bar;"#, | 653 | r#"use foo::bar;"#, |
636 | expect![[r#" | 654 | expect![[r#" |
637 | insertions: | 655 | insertions: |
638 | 656 | ||
639 | 657 | Line 0: AsFirstChild(Node([email protected])) | |
658 | -> use foo::bar; | ||
640 | 659 | ||
641 | replacements: | 660 | replacements: |
642 | 661 | ||
643 | Line 0: Node([email protected]) -> use foo::bar; | 662 | |
644 | 663 | ||
645 | deletions: | 664 | deletions: |
646 | 665 | ||
@@ -663,7 +682,7 @@ use baz;"#, | |||
663 | expect![[r#" | 682 | expect![[r#" |
664 | insertions: | 683 | insertions: |
665 | 684 | ||
666 | Line 2: Node([email protected]) | 685 | Line 2: After(Node([email protected])) |
667 | -> "\n" | 686 | -> "\n" |
668 | -> use baz; | 687 | -> use baz; |
669 | 688 | ||
@@ -691,7 +710,7 @@ use baz;"#, | |||
691 | expect![[r#" | 710 | expect![[r#" |
692 | insertions: | 711 | insertions: |
693 | 712 | ||
694 | Line 2: Token([email protected] "\n") | 713 | Line 2: After(Token([email protected] "\n")) |
695 | -> use bar; | 714 | -> use bar; |
696 | -> "\n" | 715 | -> "\n" |
697 | 716 | ||
@@ -719,7 +738,7 @@ use baz;"#, | |||
719 | expect![[r#" | 738 | expect![[r#" |
720 | insertions: | 739 | insertions: |
721 | 740 | ||
722 | Line 0: Token([email protected] "\n") | 741 | Line 0: After(Token([email protected] "\n")) |
723 | -> use foo; | 742 | -> use foo; |
724 | -> "\n" | 743 | -> "\n" |
725 | 744 | ||
@@ -735,6 +754,36 @@ use baz;"#, | |||
735 | } | 754 | } |
736 | 755 | ||
737 | #[test] | 756 | #[test] |
757 | fn first_child_insertion() { | ||
758 | mark::check!(insert_first_child); | ||
759 | check_diff( | ||
760 | r#"fn main() { | ||
761 | stdi | ||
762 | }"#, | ||
763 | r#"use foo::bar; | ||
764 | |||
765 | fn main() { | ||
766 | stdi | ||
767 | }"#, | ||
768 | expect![[r#" | ||
769 | insertions: | ||
770 | |||
771 | Line 0: AsFirstChild(Node([email protected])) | ||
772 | -> use foo::bar; | ||
773 | -> "\n\n " | ||
774 | |||
775 | replacements: | ||
776 | |||
777 | |||
778 | |||
779 | deletions: | ||
780 | |||
781 | |||
782 | "#]], | ||
783 | ); | ||
784 | } | ||
785 | |||
786 | #[test] | ||
738 | fn delete_last() { | 787 | fn delete_last() { |
739 | mark::check!(diff_delete); | 788 | mark::check!(diff_delete); |
740 | check_diff( | 789 | check_diff( |
@@ -776,7 +825,7 @@ use crate::AstNode; | |||
776 | expect![[r#" | 825 | expect![[r#" |
777 | insertions: | 826 | insertions: |
778 | 827 | ||
779 | Line 1: Node([email protected]) | 828 | Line 1: After(Node([email protected])) |
780 | -> "\n\n" | 829 | -> "\n\n" |
781 | -> use crate::AstNode; | 830 | -> use crate::AstNode; |
782 | 831 | ||
@@ -842,10 +891,10 @@ use std::ops::{self, RangeInclusive}; | |||
842 | expect![[r#" | 891 | expect![[r#" |
843 | insertions: | 892 | insertions: |
844 | 893 | ||
845 | Line 2: Node([email protected]) | 894 | Line 2: After(Node([email protected])) |
846 | -> :: | 895 | -> :: |
847 | -> fmt | 896 | -> fmt |
848 | Line 6: Token([email protected] "\n") | 897 | Line 6: After(Token([email protected] "\n")) |
849 | -> use std::hash::BuildHasherDefault; | 898 | -> use std::hash::BuildHasherDefault; |
850 | -> "\n" | 899 | -> "\n" |
851 | -> use std::ops::{self, RangeInclusive}; | 900 | -> use std::ops::{self, RangeInclusive}; |
@@ -889,14 +938,14 @@ fn main() { | |||
889 | expect![[r#" | 938 | expect![[r#" |
890 | insertions: | 939 | insertions: |
891 | 940 | ||
892 | Line 3: Node([email protected]) | 941 | Line 3: After(Node([email protected])) |
893 | -> " " | 942 | -> " " |
894 | -> match Err(92) { | 943 | -> match Err(92) { |
895 | Ok(it) => it, | 944 | Ok(it) => it, |
896 | _ => return, | 945 | _ => return, |
897 | } | 946 | } |
898 | -> ; | 947 | -> ; |
899 | Line 3: Node([email protected]) | 948 | Line 3: After(Node([email protected])) |
900 | -> "\n " | 949 | -> "\n " |
901 | -> foo(x); | 950 | -> foo(x); |
902 | 951 | ||
@@ -931,14 +980,18 @@ fn main() { | |||
931 | _ => format!("{}", syn), | 980 | _ => format!("{}", syn), |
932 | }; | 981 | }; |
933 | 982 | ||
934 | let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| { | 983 | let insertions = |
935 | f(&format!( | 984 | diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> { |
936 | "Line {}: {:?}\n-> {}", | 985 | f(&format!( |
937 | line_number(k), | 986 | "Line {}: {:?}\n-> {}", |
938 | k, | 987 | line_number(match k { |
939 | v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) | 988 | super::TreeDiffInsertPos::After(syn) => syn, |
940 | )) | 989 | super::TreeDiffInsertPos::AsFirstChild(syn) => syn, |
941 | }); | 990 | }), |
991 | k, | ||
992 | v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) | ||
993 | )) | ||
994 | }); | ||
942 | 995 | ||
943 | let replacements = diff | 996 | let replacements = diff |
944 | .replacements | 997 | .replacements |