aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs6
-rw-r--r--crates/ra_assists/src/assists/add_derive.rs2
-rw-r--r--crates/ra_assists/src/assists/add_missing_impl_members.rs36
-rw-r--r--crates/ra_assists/src/assists/move_bounds.rs12
-rw-r--r--crates/ra_assists/src/ast_editor.rs247
-rw-r--r--crates/ra_assists/src/lib.rs1
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;
4use ra_db::FileRange; 4use ra_db::FileRange;
5use ra_fmt::{leading_indent, reindent}; 5use ra_fmt::{leading_indent, reindent};
6use ra_syntax::{ 6use 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
3use hir::{db::HirDatabase, HasSource}; 3use hir::{db::HirDatabase, HasSource};
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, make, AstNode, NameOwner}, 5 ast::{self, edit, make, AstNode, NameOwner},
6 SmolStr, 6 SmolStr,
7}; 7};
8 8
9use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
10 10
11#[derive(PartialEq)] 11#[derive(PartialEq)]
12enum AddMissingImplMembersMode { 12enum 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
98fn 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
104fn add_body(fn_def: ast::FnDef) -> ast::FnDef { 100fn 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
3use hir::db::HirDatabase; 3use hir::db::HirDatabase;
4use ra_syntax::{ 4use 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
10use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; 10use crate::{Assist, AssistCtx, AssistId};
11 11
12pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 12pub(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
3use std::{iter, ops::RangeInclusive};
4
5use arrayvec::ArrayVec;
6use rustc_hash::FxHashMap;
7
8use ra_fmt::leading_indent;
9use ra_syntax::{
10 algo,
11 ast::{self, make::tokens, TypeBoundsOwner},
12 AstNode, Direction, InsertPosition, SyntaxElement,
13 SyntaxKind::*,
14 T,
15};
16use ra_text_edit::TextEditBuilder;
17
18pub struct AstEditor<N: AstNode> {
19 original_ast: N,
20 ast: N,
21}
22
23impl<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
106impl 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
184impl 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
217impl 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
234impl 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
8mod assist_ctx; 8mod assist_ctx;
9mod marks; 9mod marks;
10pub mod ast_editor;
11 10
12use hir::db::HirDatabase; 11use hir::db::HirDatabase;
13use itertools::Itertools; 12use itertools::Itertools;