diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 6 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/add_derive.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/add_missing_impl_members.rs | 36 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/move_bounds.rs | 12 | ||||
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 247 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 1 |
6 files changed, 27 insertions, 277 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index c8ba4f035..189cad7d0 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -4,7 +4,7 @@ use hir::db::HirDatabase; | |||
4 | use ra_db::FileRange; | 4 | use ra_db::FileRange; |
5 | use ra_fmt::{leading_indent, reindent}; | 5 | use ra_fmt::{leading_indent, reindent}; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{self, find_covering_element, find_node_at_offset}, |
8 | AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit, | 8 | AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit, |
9 | TokenAtOffset, | 9 | TokenAtOffset, |
10 | }; | 10 | }; |
@@ -179,6 +179,10 @@ impl AssistBuilder { | |||
179 | &mut self.edit | 179 | &mut self.edit |
180 | } | 180 | } |
181 | 181 | ||
182 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | ||
183 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
184 | } | ||
185 | |||
182 | fn build(self) -> AssistAction { | 186 | fn build(self) -> AssistAction { |
183 | AssistAction { | 187 | AssistAction { |
184 | edit: self.edit.finish(), | 188 | edit: self.edit.finish(), |
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs index 45814838f..77ecc33c9 100644 --- a/crates/ra_assists/src/assists/add_derive.rs +++ b/crates/ra_assists/src/assists/add_derive.rs | |||
@@ -15,7 +15,7 @@ pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> | |||
15 | ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| { | 15 | ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| { |
16 | let derive_attr = nominal | 16 | let derive_attr = nominal |
17 | .attrs() | 17 | .attrs() |
18 | .filter_map(|x| x.as_call()) | 18 | .filter_map(|x| x.as_simple_call()) |
19 | .filter(|(name, _arg)| name == "derive") | 19 | .filter(|(name, _arg)| name == "derive") |
20 | .map(|(_name, arg)| arg) | 20 | .map(|(_name, arg)| arg) |
21 | .next(); | 21 | .next(); |
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index 9edf1b21a..565b96fb5 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -2,11 +2,11 @@ | |||
2 | 2 | ||
3 | use hir::{db::HirDatabase, HasSource}; | 3 | use hir::{db::HirDatabase, HasSource}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, make, AstNode, NameOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner}, |
6 | SmolStr, | 6 | SmolStr, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; | 9 | use crate::{Assist, AssistCtx, AssistId}; |
10 | 10 | ||
11 | #[derive(PartialEq)] | 11 | #[derive(PartialEq)] |
12 | enum AddMissingImplMembersMode { | 12 | enum AddMissingImplMembersMode { |
@@ -77,30 +77,26 @@ fn add_missing_impl_members_inner( | |||
77 | 77 | ||
78 | ctx.add_action(AssistId(assist_id), label, |edit| { | 78 | ctx.add_action(AssistId(assist_id), label, |edit| { |
79 | let n_existing_items = impl_item_list.impl_items().count(); | 79 | let n_existing_items = impl_item_list.impl_items().count(); |
80 | let items = missing_items.into_iter().map(|it| match it { | 80 | let items = missing_items |
81 | ast::ImplItem::FnDef(def) => strip_docstring(add_body(def).into()), | 81 | .into_iter() |
82 | _ => strip_docstring(it), | 82 | .map(|it| match it { |
83 | }); | 83 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
84 | let mut ast_editor = AstEditor::new(impl_item_list); | 84 | _ => it, |
85 | 85 | }) | |
86 | ast_editor.append_items(items); | 86 | .map(|it| edit::strip_attrs_and_docs(&it)); |
87 | 87 | let new_impl_item_list = impl_item_list.append_items(items); | |
88 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); | 88 | let cursor_position = { |
89 | let cursor_position = first_new_item.syntax().text_range().start(); | 89 | let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); |
90 | ast_editor.into_text_edit(edit.text_edit_builder()); | 90 | first_new_item.syntax().text_range().start() |
91 | 91 | }; | |
92 | |||
93 | edit.replace_ast(impl_item_list, new_impl_item_list); | ||
92 | edit.set_cursor(cursor_position); | 94 | edit.set_cursor(cursor_position); |
93 | }); | 95 | }); |
94 | 96 | ||
95 | ctx.build() | 97 | ctx.build() |
96 | } | 98 | } |
97 | 99 | ||
98 | fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem { | ||
99 | let mut ast_editor = AstEditor::new(item); | ||
100 | ast_editor.strip_attrs_and_docs(); | ||
101 | ast_editor.ast().to_owned() | ||
102 | } | ||
103 | |||
104 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | 100 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { |
105 | if fn_def.body().is_none() { | 101 | if fn_def.body().is_none() { |
106 | fn_def.with_body(make::block_from_expr(make::expr_unimplemented())) | 102 | fn_def.with_body(make::block_from_expr(make::expr_unimplemented())) |
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs index 32fab03c9..f791d22b0 100644 --- a/crates/ra_assists/src/assists/move_bounds.rs +++ b/crates/ra_assists/src/assists/move_bounds.rs | |||
@@ -2,12 +2,12 @@ | |||
2 | 2 | ||
3 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, make, AstNode, NameOwner, TypeBoundsOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner, TypeBoundsOwner}, |
6 | SyntaxElement, | 6 | SyntaxElement, |
7 | SyntaxKind::*, | 7 | SyntaxKind::*, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; | 10 | use crate::{Assist, AssistCtx, AssistId}; |
11 | 11 | ||
12 | pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 12 | pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
13 | let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?; | 13 | let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?; |
@@ -41,14 +41,12 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) | |||
41 | .type_params() | 41 | .type_params() |
42 | .filter(|it| it.type_bound_list().is_some()) | 42 | .filter(|it| it.type_bound_list().is_some()) |
43 | .map(|type_param| { | 43 | .map(|type_param| { |
44 | let without_bounds = | 44 | let without_bounds = type_param.remove_bounds(); |
45 | AstEditor::new(type_param.clone()).remove_bounds().ast().clone(); | ||
46 | (type_param, without_bounds) | 45 | (type_param, without_bounds) |
47 | }); | 46 | }); |
48 | 47 | ||
49 | let mut ast_editor = AstEditor::new(type_param_list.clone()); | 48 | let new_type_param_list = edit::replace_descendants(&type_param_list, new_params); |
50 | ast_editor.replace_descendants(new_params); | 49 | edit.replace_ast(type_param_list.clone(), new_type_param_list); |
51 | ast_editor.into_text_edit(edit.text_edit_builder()); | ||
52 | 50 | ||
53 | let where_clause = { | 51 | let where_clause = { |
54 | let predicates = type_param_list.type_params().filter_map(build_predicate); | 52 | let predicates = type_param_list.type_params().filter_map(build_predicate); |
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs deleted file mode 100644 index 2de71a0cb..000000000 --- a/crates/ra_assists/src/ast_editor.rs +++ /dev/null | |||
@@ -1,247 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::{iter, ops::RangeInclusive}; | ||
4 | |||
5 | use arrayvec::ArrayVec; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use ra_fmt::leading_indent; | ||
9 | use ra_syntax::{ | ||
10 | algo, | ||
11 | ast::{self, make::tokens, TypeBoundsOwner}, | ||
12 | AstNode, Direction, InsertPosition, SyntaxElement, | ||
13 | SyntaxKind::*, | ||
14 | T, | ||
15 | }; | ||
16 | use ra_text_edit::TextEditBuilder; | ||
17 | |||
18 | pub struct AstEditor<N: AstNode> { | ||
19 | original_ast: N, | ||
20 | ast: N, | ||
21 | } | ||
22 | |||
23 | impl<N: AstNode> AstEditor<N> { | ||
24 | pub fn new(node: N) -> AstEditor<N> | ||
25 | where | ||
26 | N: Clone, | ||
27 | { | ||
28 | AstEditor { original_ast: node.clone(), ast: node } | ||
29 | } | ||
30 | |||
31 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | ||
32 | for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) { | ||
33 | builder.replace(from.text_range(), to.to_string()) | ||
34 | } | ||
35 | } | ||
36 | |||
37 | pub fn ast(&self) -> &N { | ||
38 | &self.ast | ||
39 | } | ||
40 | |||
41 | pub fn replace_descendants<T: AstNode>( | ||
42 | &mut self, | ||
43 | replacement_map: impl Iterator<Item = (T, T)>, | ||
44 | ) -> &mut Self { | ||
45 | let map = replacement_map | ||
46 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) | ||
47 | .collect::<FxHashMap<_, _>>(); | ||
48 | let new_syntax = algo::replace_descendants(self.ast.syntax(), &map); | ||
49 | self.ast = N::cast(new_syntax).unwrap(); | ||
50 | self | ||
51 | } | ||
52 | |||
53 | #[must_use] | ||
54 | fn insert_children( | ||
55 | &self, | ||
56 | position: InsertPosition<SyntaxElement>, | ||
57 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
58 | ) -> N { | ||
59 | let new_syntax = algo::insert_children(self.ast().syntax(), position, &mut to_insert); | ||
60 | N::cast(new_syntax).unwrap() | ||
61 | } | ||
62 | |||
63 | #[must_use] | ||
64 | fn replace_children( | ||
65 | &self, | ||
66 | to_delete: RangeInclusive<SyntaxElement>, | ||
67 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
68 | ) -> N { | ||
69 | let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); | ||
70 | N::cast(new_syntax).unwrap() | ||
71 | } | ||
72 | |||
73 | fn do_make_multiline(&mut self) { | ||
74 | let l_curly = | ||
75 | match self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
76 | Some(it) => it, | ||
77 | None => return, | ||
78 | }; | ||
79 | let sibling = match l_curly.next_sibling_or_token() { | ||
80 | Some(it) => it, | ||
81 | None => return, | ||
82 | }; | ||
83 | let existing_ws = match sibling.as_token() { | ||
84 | None => None, | ||
85 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
86 | Some(ws) => { | ||
87 | if ws.text().contains('\n') { | ||
88 | return; | ||
89 | } | ||
90 | Some(ws.clone()) | ||
91 | } | ||
92 | }; | ||
93 | |||
94 | let indent = leading_indent(self.ast().syntax()).unwrap_or("".into()); | ||
95 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
96 | let to_insert = iter::once(ws.ws().into()); | ||
97 | self.ast = match existing_ws { | ||
98 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), | ||
99 | Some(ws) => { | ||
100 | self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) | ||
101 | } | ||
102 | }; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl AstEditor<ast::RecordFieldList> { | ||
107 | pub fn append_field(&mut self, field: &ast::RecordField) { | ||
108 | self.insert_field(InsertPosition::Last, field) | ||
109 | } | ||
110 | |||
111 | pub fn insert_field( | ||
112 | &mut self, | ||
113 | position: InsertPosition<&'_ ast::RecordField>, | ||
114 | field: &ast::RecordField, | ||
115 | ) { | ||
116 | let is_multiline = self.ast().syntax().text().contains_char('\n'); | ||
117 | let ws; | ||
118 | let space = if is_multiline { | ||
119 | ws = tokens::WsBuilder::new(&format!( | ||
120 | "\n{} ", | ||
121 | leading_indent(self.ast().syntax()).unwrap_or("".into()) | ||
122 | )); | ||
123 | ws.ws() | ||
124 | } else { | ||
125 | tokens::single_space() | ||
126 | }; | ||
127 | |||
128 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | ||
129 | to_insert.push(space.into()); | ||
130 | to_insert.push(field.syntax().clone().into()); | ||
131 | to_insert.push(tokens::comma().into()); | ||
132 | |||
133 | macro_rules! after_l_curly { | ||
134 | () => {{ | ||
135 | let anchor = match self.l_curly() { | ||
136 | Some(it) => it, | ||
137 | None => return, | ||
138 | }; | ||
139 | InsertPosition::After(anchor) | ||
140 | }}; | ||
141 | } | ||
142 | |||
143 | macro_rules! after_field { | ||
144 | ($anchor:expr) => { | ||
145 | if let Some(comma) = $anchor | ||
146 | .syntax() | ||
147 | .siblings_with_tokens(Direction::Next) | ||
148 | .find(|it| it.kind() == T![,]) | ||
149 | { | ||
150 | InsertPosition::After(comma) | ||
151 | } else { | ||
152 | to_insert.insert(0, tokens::comma().into()); | ||
153 | InsertPosition::After($anchor.syntax().clone().into()) | ||
154 | } | ||
155 | }; | ||
156 | }; | ||
157 | |||
158 | let position = match position { | ||
159 | InsertPosition::First => after_l_curly!(), | ||
160 | InsertPosition::Last => { | ||
161 | if !is_multiline { | ||
162 | // don't insert comma before curly | ||
163 | to_insert.pop(); | ||
164 | } | ||
165 | match self.ast().fields().last() { | ||
166 | Some(it) => after_field!(it), | ||
167 | None => after_l_curly!(), | ||
168 | } | ||
169 | } | ||
170 | InsertPosition::Before(anchor) => { | ||
171 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
172 | } | ||
173 | InsertPosition::After(anchor) => after_field!(anchor), | ||
174 | }; | ||
175 | |||
176 | self.ast = self.insert_children(position, to_insert.iter().cloned()); | ||
177 | } | ||
178 | |||
179 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
180 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
181 | } | ||
182 | } | ||
183 | |||
184 | impl AstEditor<ast::ItemList> { | ||
185 | pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { | ||
186 | if !self.ast().syntax().text().contains_char('\n') { | ||
187 | self.do_make_multiline(); | ||
188 | } | ||
189 | items.for_each(|it| self.append_item(it)); | ||
190 | } | ||
191 | |||
192 | pub fn append_item(&mut self, item: ast::ImplItem) { | ||
193 | let (indent, position) = match self.ast().impl_items().last() { | ||
194 | Some(it) => ( | ||
195 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
196 | InsertPosition::After(it.syntax().clone().into()), | ||
197 | ), | ||
198 | None => match self.l_curly() { | ||
199 | Some(it) => ( | ||
200 | " ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(), | ||
201 | InsertPosition::After(it), | ||
202 | ), | ||
203 | None => return, | ||
204 | }, | ||
205 | }; | ||
206 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
207 | let to_insert: ArrayVec<[SyntaxElement; 2]> = | ||
208 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
209 | self.ast = self.insert_children(position, to_insert.into_iter()); | ||
210 | } | ||
211 | |||
212 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
213 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
214 | } | ||
215 | } | ||
216 | |||
217 | impl AstEditor<ast::ImplItem> { | ||
218 | pub fn strip_attrs_and_docs(&mut self) { | ||
219 | while let Some(start) = self | ||
220 | .ast() | ||
221 | .syntax() | ||
222 | .children_with_tokens() | ||
223 | .find(|it| it.kind() == ATTR || it.kind() == COMMENT) | ||
224 | { | ||
225 | let end = match &start.next_sibling_or_token() { | ||
226 | Some(el) if el.kind() == WHITESPACE => el.clone(), | ||
227 | Some(_) | None => start.clone(), | ||
228 | }; | ||
229 | self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | |||
234 | impl AstEditor<ast::TypeParam> { | ||
235 | pub fn remove_bounds(&mut self) -> &mut Self { | ||
236 | let colon = match self.ast.colon_token() { | ||
237 | Some(it) => it, | ||
238 | None => return self, | ||
239 | }; | ||
240 | let end = match self.ast.type_bound_list() { | ||
241 | Some(it) => it.syntax().clone().into(), | ||
242 | None => colon.clone().into(), | ||
243 | }; | ||
244 | self.ast = self.replace_children(RangeInclusive::new(colon.into(), end), iter::empty()); | ||
245 | self | ||
246 | } | ||
247 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3ca3320f7..91b2a1dce 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -7,7 +7,6 @@ | |||
7 | 7 | ||
8 | mod assist_ctx; | 8 | mod assist_ctx; |
9 | mod marks; | 9 | mod marks; |
10 | pub mod ast_editor; | ||
11 | 10 | ||
12 | use hir::db::HirDatabase; | 11 | use hir::db::HirDatabase; |
13 | use itertools::Itertools; | 12 | use itertools::Itertools; |