aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/ast_editor.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-22 11:01:33 +0100
committerAleksey Kladov <[email protected]>2019-04-22 11:01:33 +0100
commit268e739c94d2e9edbb45374dfcc252b1648d3181 (patch)
tree25820553d53e8a638003e315076c6c074464fd9e /crates/ra_assists/src/ast_editor.rs
parentb811922a530b7b22469b386a521946f1003911c7 (diff)
move add_missing_members to structured editing API
Currently, this is more code, and we also loose auto-indenting of bodies, but, long-term, this is the right approach
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r--crates/ra_assists/src/ast_editor.rs158
1 files changed, 140 insertions, 18 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index 13ee82879..283b280b6 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -1,4 +1,4 @@
1use std::iter; 1use std::{iter, ops::RangeInclusive};
2 2
3use arrayvec::ArrayVec; 3use arrayvec::ArrayVec;
4use ra_text_edit::TextEditBuilder; 4use ra_text_edit::TextEditBuilder;
@@ -26,6 +26,7 @@ impl<N: AstNode> AstEditor<N> {
26 &*self.ast 26 &*self.ast
27 } 27 }
28 28
29 #[must_use]
29 fn insert_children<'a>( 30 fn insert_children<'a>(
30 &self, 31 &self,
31 position: InsertPosition<SyntaxElement<'_>>, 32 position: InsertPosition<SyntaxElement<'_>>,
@@ -34,31 +35,55 @@ impl<N: AstNode> AstEditor<N> {
34 let new_syntax = self.ast().syntax().insert_children(position, to_insert); 35 let new_syntax = self.ast().syntax().insert_children(position, to_insert);
35 N::cast(&new_syntax).unwrap().to_owned() 36 N::cast(&new_syntax).unwrap().to_owned()
36 } 37 }
37}
38 38
39impl AstEditor<ast::NamedFieldList> { 39 #[must_use]
40 pub fn append_field(&mut self, field: &ast::NamedField) { 40 fn replace_children<'a>(
41 self.insert_field(InsertPosition::Last, field) 41 &self,
42 to_delete: RangeInclusive<SyntaxElement<'_>>,
43 to_insert: impl Iterator<Item = SyntaxElement<'a>>,
44 ) -> TreeArc<N> {
45 let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert);
46 N::cast(&new_syntax).unwrap().to_owned()
42 } 47 }
43 48
44 pub fn make_multiline(&mut self) { 49 fn do_make_multiline(&mut self) {
45 let l_curly = match self.l_curly() { 50 let l_curly =
46 Some(it) => it, 51 match self.ast().syntax().children_with_tokens().find(|it| it.kind() == L_CURLY) {
47 None => return, 52 Some(it) => it,
48 }; 53 None => return,
54 };
49 let sibling = match l_curly.next_sibling_or_token() { 55 let sibling = match l_curly.next_sibling_or_token() {
50 Some(it) => it, 56 Some(it) => it,
51 None => return, 57 None => return,
52 }; 58 };
53 if sibling.as_token().map(|it| it.text().contains('\n')) == Some(true) { 59 let existing_ws = match sibling.as_token() {
54 return; 60 None => None,
55 } 61 Some(tok) if tok.kind() != WHITESPACE => None,
62 Some(ws) => {
63 if ws.text().contains('\n') {
64 return;
65 }
66 Some(ws)
67 }
68 };
69
70 let indent = leading_indent(self.ast().syntax()).unwrap_or("");
71 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
72 let to_insert = iter::once(ws.ws().into());
73 self.ast = match existing_ws {
74 None => self.insert_children(InsertPosition::After(l_curly), to_insert),
75 Some(ws) => self.replace_children(RangeInclusive::new(ws.into(), ws.into()), to_insert),
76 };
77 }
78}
79
80impl AstEditor<ast::NamedFieldList> {
81 pub fn append_field(&mut self, field: &ast::NamedField) {
82 self.insert_field(InsertPosition::Last, field)
83 }
56 84
57 let ws = tokens::WsBuilder::new(&format!( 85 pub fn make_multiline(&mut self) {
58 "\n{}", 86 self.do_make_multiline()
59 leading_indent(self.ast().syntax()).unwrap_or("")
60 ));
61 self.ast = self.insert_children(InsertPosition::After(l_curly), iter::once(ws.ws().into()));
62 } 87 }
63 88
64 pub fn insert_field( 89 pub fn insert_field(
@@ -132,6 +157,79 @@ impl AstEditor<ast::NamedFieldList> {
132 } 157 }
133} 158}
134 159
160impl AstEditor<ast::ItemList> {
161 pub fn make_multiline(&mut self) {
162 self.do_make_multiline()
163 }
164
165 pub fn append_functions<'a>(&mut self, fns: impl Iterator<Item = &'a ast::FnDef>) {
166 fns.for_each(|it| self.append_function(it))
167 }
168
169 pub fn append_function(&mut self, fn_def: &ast::FnDef) {
170 let (indent, position) = match self.ast().impl_items().last() {
171 Some(it) => (
172 leading_indent(it.syntax()).unwrap_or("").to_string(),
173 InsertPosition::After(it.syntax().into()),
174 ),
175 None => match self.l_curly() {
176 Some(it) => (
177 " ".to_string() + leading_indent(self.ast().syntax()).unwrap_or(""),
178 InsertPosition::After(it),
179 ),
180 None => return,
181 },
182 };
183 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
184 let to_insert: ArrayVec<[SyntaxElement; 2]> =
185 [ws.ws().into(), fn_def.syntax().into()].into();
186 self.ast = self.insert_children(position, to_insert.into_iter());
187 }
188
189 fn l_curly(&self) -> Option<SyntaxElement> {
190 self.ast().syntax().children_with_tokens().find(|it| it.kind() == L_CURLY)
191 }
192}
193
194impl AstEditor<ast::FnDef> {
195 pub fn set_body(&mut self, body: &ast::Block) {
196 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
197 let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() {
198 old_body.syntax().into()
199 } else if let Some(semi) = self.ast().semicolon_token() {
200 to_insert.push(tokens::single_space().into());
201 semi.into()
202 } else {
203 to_insert.push(tokens::single_space().into());
204 to_insert.push(body.syntax().into());
205 self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter());
206 return;
207 };
208 to_insert.push(body.syntax().into());
209 let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi);
210 self.ast = self.replace_children(replace_range, to_insert.into_iter())
211 }
212
213 pub fn strip_attrs_and_docs(&mut self) {
214 loop {
215 if let Some(start) = self
216 .ast()
217 .syntax()
218 .children_with_tokens()
219 .find(|it| it.kind() == ATTR || it.kind() == COMMENT)
220 {
221 let end = match start.next_sibling_or_token() {
222 Some(el) if el.kind() == WHITESPACE => el,
223 Some(_) | None => start,
224 };
225 self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
226 } else {
227 break;
228 }
229 }
230 }
231}
232
135pub struct AstBuilder<N: AstNode> { 233pub struct AstBuilder<N: AstNode> {
136 _phantom: std::marker::PhantomData<N>, 234 _phantom: std::marker::PhantomData<N>,
137} 235}
@@ -149,6 +247,16 @@ impl AstBuilder<ast::NamedField> {
149 } 247 }
150} 248}
151 249
250impl AstBuilder<ast::Block> {
251 fn from_text(text: &str) -> TreeArc<ast::Block> {
252 ast_node_from_file_text(&format!("fn f() {}", text))
253 }
254
255 pub fn single_expr(e: &ast::Expr) -> TreeArc<ast::Block> {
256 Self::from_text(&format!("{{ {} }}", e.syntax()))
257 }
258}
259
152impl AstBuilder<ast::Expr> { 260impl AstBuilder<ast::Expr> {
153 fn from_text(text: &str) -> TreeArc<ast::Expr> { 261 fn from_text(text: &str) -> TreeArc<ast::Expr> {
154 ast_node_from_file_text(&format!("fn f() {{ {}; }}", text)) 262 ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
@@ -157,6 +265,10 @@ impl AstBuilder<ast::Expr> {
157 pub fn unit() -> TreeArc<ast::Expr> { 265 pub fn unit() -> TreeArc<ast::Expr> {
158 Self::from_text("()") 266 Self::from_text("()")
159 } 267 }
268
269 pub fn unimplemented() -> TreeArc<ast::Expr> {
270 Self::from_text("unimplemented!()")
271 }
160} 272}
161 273
162impl AstBuilder<ast::NameRef> { 274impl AstBuilder<ast::NameRef> {
@@ -197,6 +309,16 @@ mod tokens {
197 .unwrap() 309 .unwrap()
198 } 310 }
199 311
312 #[allow(unused)]
313 pub(crate) fn single_newline() -> SyntaxToken<'static> {
314 SOURCE_FILE
315 .syntax()
316 .descendants_with_tokens()
317 .filter_map(|it| it.as_token())
318 .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
319 .unwrap()
320 }
321
200 pub(crate) struct WsBuilder(TreeArc<SourceFile>); 322 pub(crate) struct WsBuilder(TreeArc<SourceFile>);
201 323
202 impl WsBuilder { 324 impl WsBuilder {