diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-25 21:44:20 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-25 21:44:20 +0100 |
commit | 870ce4b1a50a07e3a536ab26215804acdfc9ba8a (patch) | |
tree | 2ea0c53b6d7b09998dbc0b32d4552aeb30d210ed /crates/ra_assists/src/ast_editor.rs | |
parent | 0d277faf6c4052dcc80037fc43b4986980d0814b (diff) | |
parent | a525e830a62272d21fbb0fb1c20bfa865791512d (diff) |
Merge #1912
1912: add new editing API, suitable for modifying several nodes at once r=viorina a=matklad
r? @viorina
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 68 |
1 files changed, 57 insertions, 11 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 55c0aa59f..4e253f0a4 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs | |||
@@ -1,11 +1,13 @@ | |||
1 | use std::{iter, ops::RangeInclusive}; | 1 | use std::{iter, ops::RangeInclusive}; |
2 | 2 | ||
3 | use arrayvec::ArrayVec; | 3 | use arrayvec::ArrayVec; |
4 | use rustc_hash::FxHashMap; | ||
4 | 5 | ||
5 | use ra_fmt::leading_indent; | 6 | use ra_fmt::leading_indent; |
6 | use ra_syntax::{ | 7 | use ra_syntax::{ |
7 | algo::{insert_children, replace_children}, | 8 | algo, |
8 | ast, AstNode, Direction, InsertPosition, SyntaxElement, | 9 | ast::{self, TypeBoundsOwner}, |
10 | AstNode, Direction, InsertPosition, NodeOrToken, SyntaxElement, | ||
9 | SyntaxKind::*, | 11 | SyntaxKind::*, |
10 | T, | 12 | T, |
11 | }; | 13 | }; |
@@ -27,26 +29,55 @@ impl<N: AstNode> AstEditor<N> { | |||
27 | } | 29 | } |
28 | 30 | ||
29 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | 31 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { |
30 | // FIXME: compute a more fine-grained diff here. | 32 | // FIXME: this is both horrible inefficient and gives larger than |
31 | // If *you* know a nice algorithm to compute diff between two syntax | 33 | // necessary diff. I bet there's a cool algorithm to diff trees properly. |
32 | // tree, tell me about it! | 34 | go(builder, self.original_ast.syntax().clone().into(), self.ast().syntax().clone().into()); |
33 | builder.replace( | 35 | |
34 | self.original_ast.syntax().text_range(), | 36 | fn go(buf: &mut TextEditBuilder, lhs: SyntaxElement, rhs: SyntaxElement) { |
35 | self.ast().syntax().text().to_string(), | 37 | if lhs.kind() == rhs.kind() && lhs.text_range().len() == rhs.text_range().len() { |
36 | ); | 38 | if match (&lhs, &rhs) { |
39 | (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => lhs.text() == rhs.text(), | ||
40 | (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), | ||
41 | _ => false, | ||
42 | } { | ||
43 | return; | ||
44 | } | ||
45 | } | ||
46 | if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) { | ||
47 | if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() { | ||
48 | for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) { | ||
49 | go(buf, lhs, rhs) | ||
50 | } | ||
51 | return; | ||
52 | } | ||
53 | } | ||
54 | buf.replace(lhs.text_range(), rhs.to_string()) | ||
55 | } | ||
37 | } | 56 | } |
38 | 57 | ||
39 | pub fn ast(&self) -> &N { | 58 | pub fn ast(&self) -> &N { |
40 | &self.ast | 59 | &self.ast |
41 | } | 60 | } |
42 | 61 | ||
62 | pub fn replace_descendants<T: AstNode>( | ||
63 | &mut self, | ||
64 | replacement_map: impl Iterator<Item = (T, T)>, | ||
65 | ) -> &mut Self { | ||
66 | let map = replacement_map | ||
67 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) | ||
68 | .collect::<FxHashMap<_, _>>(); | ||
69 | let new_syntax = algo::replace_descendants(self.ast.syntax(), &map); | ||
70 | self.ast = N::cast(new_syntax).unwrap(); | ||
71 | self | ||
72 | } | ||
73 | |||
43 | #[must_use] | 74 | #[must_use] |
44 | fn insert_children( | 75 | fn insert_children( |
45 | &self, | 76 | &self, |
46 | position: InsertPosition<SyntaxElement>, | 77 | position: InsertPosition<SyntaxElement>, |
47 | mut to_insert: impl Iterator<Item = SyntaxElement>, | 78 | mut to_insert: impl Iterator<Item = SyntaxElement>, |
48 | ) -> N { | 79 | ) -> N { |
49 | let new_syntax = insert_children(self.ast().syntax(), position, &mut to_insert); | 80 | let new_syntax = algo::insert_children(self.ast().syntax(), position, &mut to_insert); |
50 | N::cast(new_syntax).unwrap() | 81 | N::cast(new_syntax).unwrap() |
51 | } | 82 | } |
52 | 83 | ||
@@ -56,7 +87,7 @@ impl<N: AstNode> AstEditor<N> { | |||
56 | to_delete: RangeInclusive<SyntaxElement>, | 87 | to_delete: RangeInclusive<SyntaxElement>, |
57 | mut to_insert: impl Iterator<Item = SyntaxElement>, | 88 | mut to_insert: impl Iterator<Item = SyntaxElement>, |
58 | ) -> N { | 89 | ) -> N { |
59 | let new_syntax = replace_children(self.ast().syntax(), to_delete, &mut to_insert); | 90 | let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); |
60 | N::cast(new_syntax).unwrap() | 91 | N::cast(new_syntax).unwrap() |
61 | } | 92 | } |
62 | 93 | ||
@@ -240,3 +271,18 @@ impl AstEditor<ast::FnDef> { | |||
240 | self.ast = self.replace_children(replace_range, to_insert.into_iter()) | 271 | self.ast = self.replace_children(replace_range, to_insert.into_iter()) |
241 | } | 272 | } |
242 | } | 273 | } |
274 | |||
275 | impl AstEditor<ast::TypeParam> { | ||
276 | pub fn remove_bounds(&mut self) -> &mut Self { | ||
277 | let colon = match self.ast.colon_token() { | ||
278 | Some(it) => it, | ||
279 | None => return self, | ||
280 | }; | ||
281 | let end = match self.ast.type_bound_list() { | ||
282 | Some(it) => it.syntax().clone().into(), | ||
283 | None => colon.clone().into(), | ||
284 | }; | ||
285 | self.ast = self.replace_children(RangeInclusive::new(colon.into(), end), iter::empty()); | ||
286 | self | ||
287 | } | ||
288 | } | ||