diff options
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 158 |
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 @@ | |||
1 | use std::iter; | 1 | use std::{iter, ops::RangeInclusive}; |
2 | 2 | ||
3 | use arrayvec::ArrayVec; | 3 | use arrayvec::ArrayVec; |
4 | use ra_text_edit::TextEditBuilder; | 4 | use 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 | ||
39 | impl 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 | |||
80 | impl 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 | ||
160 | impl 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 | |||
194 | impl 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 | |||
135 | pub struct AstBuilder<N: AstNode> { | 233 | pub 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 | ||
250 | impl 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 | |||
152 | impl AstBuilder<ast::Expr> { | 260 | impl 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 | ||
162 | impl AstBuilder<ast::NameRef> { | 274 | impl 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 { |