diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-26 13:58:54 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-26 13:58:54 +0100 |
commit | 53a30d9e69ee5149e4fdb1c6fe4081281e879d0e (patch) | |
tree | acb80dc5a8a91853f49ddf632a461a66df180ad4 /crates/ra_syntax/src/algo.rs | |
parent | 3882231f3231db03144107f72c6052f773fe2375 (diff) | |
parent | 1a4b42400544a652a053a34263967689d47f554b (diff) |
Merge #1919
1919: move diff to ra_syntax r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/algo.rs')
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index f0ed96a17..46680a08f 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -63,6 +63,48 @@ pub enum InsertPosition<T> { | |||
63 | After(T), | 63 | After(T), |
64 | } | 64 | } |
65 | 65 | ||
66 | /// Finds minimal the diff, which, applied to `from`, will result in `to`. | ||
67 | /// | ||
68 | /// Specifically, returns a map whose keys are descendants of `from` and values | ||
69 | /// are descendants of `to`, such that `replace_descendants(from, map) == to`. | ||
70 | /// | ||
71 | /// A trivial solution is a singletom map `{ from: to }`, but this function | ||
72 | /// tries to find a more fine-grained diff. | ||
73 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> FxHashMap<SyntaxElement, SyntaxElement> { | ||
74 | let mut buf = FxHashMap::default(); | ||
75 | // FIXME: this is both horrible inefficient and gives larger than | ||
76 | // necessary diff. I bet there's a cool algorithm to diff trees properly. | ||
77 | go(&mut buf, from.clone().into(), to.clone().into()); | ||
78 | return buf; | ||
79 | |||
80 | fn go( | ||
81 | buf: &mut FxHashMap<SyntaxElement, SyntaxElement>, | ||
82 | lhs: SyntaxElement, | ||
83 | rhs: SyntaxElement, | ||
84 | ) { | ||
85 | if lhs.kind() == rhs.kind() && lhs.text_range().len() == rhs.text_range().len() { | ||
86 | if match (&lhs, &rhs) { | ||
87 | (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { | ||
88 | lhs.green() == rhs.green() || lhs.text() == rhs.text() | ||
89 | } | ||
90 | (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), | ||
91 | _ => false, | ||
92 | } { | ||
93 | return; | ||
94 | } | ||
95 | } | ||
96 | if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) { | ||
97 | if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() { | ||
98 | for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) { | ||
99 | go(buf, lhs, rhs) | ||
100 | } | ||
101 | return; | ||
102 | } | ||
103 | } | ||
104 | buf.insert(lhs, rhs); | ||
105 | } | ||
106 | } | ||
107 | |||
66 | /// Adds specified children (tokens or nodes) to the current node at the | 108 | /// Adds specified children (tokens or nodes) to the current node at the |
67 | /// specific position. | 109 | /// specific position. |
68 | /// | 110 | /// |