diff options
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 27 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 42 |
2 files changed, 45 insertions, 24 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 2936c094b..2a685f26e 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs | |||
@@ -7,7 +7,7 @@ use ra_fmt::leading_indent; | |||
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | algo, | 8 | algo, |
9 | ast::{self, TypeBoundsOwner}, | 9 | ast::{self, TypeBoundsOwner}, |
10 | AstNode, Direction, InsertPosition, NodeOrToken, SyntaxElement, | 10 | AstNode, Direction, InsertPosition, SyntaxElement, |
11 | SyntaxKind::*, | 11 | SyntaxKind::*, |
12 | T, | 12 | T, |
13 | }; | 13 | }; |
@@ -27,29 +27,8 @@ impl<N: AstNode> AstEditor<N> { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | 29 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { |
30 | // FIXME: this is both horrible inefficient and gives larger than | 30 | for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) { |
31 | // necessary diff. I bet there's a cool algorithm to diff trees properly. | 31 | builder.replace(from.text_range(), to.to_string()) |
32 | go(builder, self.original_ast.syntax().clone().into(), self.ast().syntax().clone().into()); | ||
33 | |||
34 | fn go(buf: &mut TextEditBuilder, lhs: SyntaxElement, rhs: SyntaxElement) { | ||
35 | if lhs.kind() == rhs.kind() && lhs.text_range().len() == rhs.text_range().len() { | ||
36 | if match (&lhs, &rhs) { | ||
37 | (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => lhs.text() == rhs.text(), | ||
38 | (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), | ||
39 | _ => false, | ||
40 | } { | ||
41 | return; | ||
42 | } | ||
43 | } | ||
44 | if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) { | ||
45 | if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() { | ||
46 | for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) { | ||
47 | go(buf, lhs, rhs) | ||
48 | } | ||
49 | return; | ||
50 | } | ||
51 | } | ||
52 | buf.replace(lhs.text_range(), rhs.to_string()) | ||
53 | } | 32 | } |
54 | } | 33 | } |
55 | 34 | ||
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 | /// |