diff options
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 123 |
1 files changed, 65 insertions, 58 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 7b743c9f0..5fbcadfee 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs | |||
@@ -4,18 +4,18 @@ use arrayvec::ArrayVec; | |||
4 | use hir::Name; | 4 | use hir::Name; |
5 | use ra_fmt::leading_indent; | 5 | use ra_fmt::leading_indent; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, TreeArc, T, | 7 | ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, T, |
8 | }; | 8 | }; |
9 | use ra_text_edit::TextEditBuilder; | 9 | use ra_text_edit::TextEditBuilder; |
10 | 10 | ||
11 | pub struct AstEditor<N: AstNode> { | 11 | pub struct AstEditor<N: AstNode> { |
12 | original_ast: TreeArc<N>, | 12 | original_ast: N, |
13 | ast: TreeArc<N>, | 13 | ast: N, |
14 | } | 14 | } |
15 | 15 | ||
16 | impl<N: AstNode> AstEditor<N> { | 16 | impl<N: AstNode> AstEditor<N> { |
17 | pub fn new(node: &N) -> AstEditor<N> { | 17 | pub fn new(node: N) -> AstEditor<N> { |
18 | AstEditor { original_ast: node.to_owned(), ast: node.to_owned() } | 18 | AstEditor { original_ast: node.clone(), ast: node } |
19 | } | 19 | } |
20 | 20 | ||
21 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | 21 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { |
@@ -26,27 +26,27 @@ impl<N: AstNode> AstEditor<N> { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn ast(&self) -> &N { | 28 | pub fn ast(&self) -> &N { |
29 | &*self.ast | 29 | &self.ast |
30 | } | 30 | } |
31 | 31 | ||
32 | #[must_use] | 32 | #[must_use] |
33 | fn insert_children<'a>( | 33 | fn insert_children( |
34 | &self, | 34 | &self, |
35 | position: InsertPosition<SyntaxElement<'_>>, | 35 | position: InsertPosition<SyntaxElement>, |
36 | to_insert: impl Iterator<Item = SyntaxElement<'a>>, | 36 | to_insert: impl Iterator<Item = SyntaxElement>, |
37 | ) -> TreeArc<N> { | 37 | ) -> N { |
38 | let new_syntax = self.ast().syntax().insert_children(position, to_insert); | 38 | let new_syntax = self.ast().syntax().insert_children(position, to_insert); |
39 | N::cast(&new_syntax).unwrap().to_owned() | 39 | N::cast(new_syntax).unwrap() |
40 | } | 40 | } |
41 | 41 | ||
42 | #[must_use] | 42 | #[must_use] |
43 | fn replace_children<'a>( | 43 | fn replace_children( |
44 | &self, | 44 | &self, |
45 | to_delete: RangeInclusive<SyntaxElement<'_>>, | 45 | to_delete: RangeInclusive<SyntaxElement>, |
46 | to_insert: impl Iterator<Item = SyntaxElement<'a>>, | 46 | to_insert: impl Iterator<Item = SyntaxElement>, |
47 | ) -> TreeArc<N> { | 47 | ) -> N { |
48 | let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert); | 48 | let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert); |
49 | N::cast(&new_syntax).unwrap().to_owned() | 49 | N::cast(new_syntax).unwrap() |
50 | } | 50 | } |
51 | 51 | ||
52 | fn do_make_multiline(&mut self) { | 52 | fn do_make_multiline(&mut self) { |
@@ -66,16 +66,18 @@ impl<N: AstNode> AstEditor<N> { | |||
66 | if ws.text().contains('\n') { | 66 | if ws.text().contains('\n') { |
67 | return; | 67 | return; |
68 | } | 68 | } |
69 | Some(ws) | 69 | Some(ws.clone()) |
70 | } | 70 | } |
71 | }; | 71 | }; |
72 | 72 | ||
73 | let indent = leading_indent(self.ast().syntax()).unwrap_or(""); | 73 | let indent = leading_indent(self.ast().syntax()).unwrap_or("".into()); |
74 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | 74 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); |
75 | let to_insert = iter::once(ws.ws().into()); | 75 | let to_insert = iter::once(ws.ws().into()); |
76 | self.ast = match existing_ws { | 76 | self.ast = match existing_ws { |
77 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), | 77 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), |
78 | Some(ws) => self.replace_children(RangeInclusive::new(ws.into(), ws.into()), to_insert), | 78 | Some(ws) => { |
79 | self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) | ||
80 | } | ||
79 | }; | 81 | }; |
80 | } | 82 | } |
81 | } | 83 | } |
@@ -95,7 +97,7 @@ impl AstEditor<ast::NamedFieldList> { | |||
95 | let space = if is_multiline { | 97 | let space = if is_multiline { |
96 | ws = tokens::WsBuilder::new(&format!( | 98 | ws = tokens::WsBuilder::new(&format!( |
97 | "\n{} ", | 99 | "\n{} ", |
98 | leading_indent(self.ast().syntax()).unwrap_or("") | 100 | leading_indent(self.ast().syntax()).unwrap_or("".into()) |
99 | )); | 101 | )); |
100 | ws.ws() | 102 | ws.ws() |
101 | } else { | 103 | } else { |
@@ -104,7 +106,7 @@ impl AstEditor<ast::NamedFieldList> { | |||
104 | 106 | ||
105 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | 107 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); |
106 | to_insert.push(space.into()); | 108 | to_insert.push(space.into()); |
107 | to_insert.push(field.syntax().into()); | 109 | to_insert.push(field.syntax().clone().into()); |
108 | to_insert.push(tokens::comma().into()); | 110 | to_insert.push(tokens::comma().into()); |
109 | 111 | ||
110 | macro_rules! after_l_curly { | 112 | macro_rules! after_l_curly { |
@@ -127,7 +129,7 @@ impl AstEditor<ast::NamedFieldList> { | |||
127 | InsertPosition::After(comma) | 129 | InsertPosition::After(comma) |
128 | } else { | 130 | } else { |
129 | to_insert.insert(0, tokens::comma().into()); | 131 | to_insert.insert(0, tokens::comma().into()); |
130 | InsertPosition::After($anchor.syntax().into()) | 132 | InsertPosition::After($anchor.syntax().clone().into()) |
131 | } | 133 | } |
132 | }; | 134 | }; |
133 | }; | 135 | }; |
@@ -144,7 +146,9 @@ impl AstEditor<ast::NamedFieldList> { | |||
144 | None => after_l_curly!(), | 146 | None => after_l_curly!(), |
145 | } | 147 | } |
146 | } | 148 | } |
147 | InsertPosition::Before(anchor) => InsertPosition::Before(anchor.syntax().into()), | 149 | InsertPosition::Before(anchor) => { |
150 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
151 | } | ||
148 | InsertPosition::After(anchor) => after_field!(anchor), | 152 | InsertPosition::After(anchor) => after_field!(anchor), |
149 | }; | 153 | }; |
150 | 154 | ||
@@ -157,7 +161,7 @@ impl AstEditor<ast::NamedFieldList> { | |||
157 | } | 161 | } |
158 | 162 | ||
159 | impl AstEditor<ast::ItemList> { | 163 | impl AstEditor<ast::ItemList> { |
160 | pub fn append_items<'a>(&mut self, items: impl Iterator<Item = &'a ast::ImplItem>) { | 164 | pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { |
161 | let n_existing_items = self.ast().impl_items().count(); | 165 | let n_existing_items = self.ast().impl_items().count(); |
162 | if n_existing_items == 0 { | 166 | if n_existing_items == 0 { |
163 | self.do_make_multiline(); | 167 | self.do_make_multiline(); |
@@ -165,22 +169,23 @@ impl AstEditor<ast::ItemList> { | |||
165 | items.for_each(|it| self.append_item(it)); | 169 | items.for_each(|it| self.append_item(it)); |
166 | } | 170 | } |
167 | 171 | ||
168 | pub fn append_item(&mut self, item: &ast::ImplItem) { | 172 | pub fn append_item(&mut self, item: ast::ImplItem) { |
169 | let (indent, position) = match self.ast().impl_items().last() { | 173 | let (indent, position) = match self.ast().impl_items().last() { |
170 | Some(it) => ( | 174 | Some(it) => ( |
171 | leading_indent(it.syntax()).unwrap_or("").to_string(), | 175 | leading_indent(it.syntax()).unwrap_or_default().to_string(), |
172 | InsertPosition::After(it.syntax().into()), | 176 | InsertPosition::After(it.syntax().clone().into()), |
173 | ), | 177 | ), |
174 | None => match self.l_curly() { | 178 | None => match self.l_curly() { |
175 | Some(it) => ( | 179 | Some(it) => ( |
176 | " ".to_string() + leading_indent(self.ast().syntax()).unwrap_or(""), | 180 | " ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(), |
177 | InsertPosition::After(it), | 181 | InsertPosition::After(it), |
178 | ), | 182 | ), |
179 | None => return, | 183 | None => return, |
180 | }, | 184 | }, |
181 | }; | 185 | }; |
182 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | 186 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); |
183 | let to_insert: ArrayVec<[SyntaxElement; 2]> = [ws.ws().into(), item.syntax().into()].into(); | 187 | let to_insert: ArrayVec<[SyntaxElement; 2]> = |
188 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
184 | self.ast = self.insert_children(position, to_insert.into_iter()); | 189 | self.ast = self.insert_children(position, to_insert.into_iter()); |
185 | } | 190 | } |
186 | 191 | ||
@@ -197,9 +202,9 @@ impl AstEditor<ast::ImplItem> { | |||
197 | .children_with_tokens() | 202 | .children_with_tokens() |
198 | .find(|it| it.kind() == ATTR || it.kind() == COMMENT) | 203 | .find(|it| it.kind() == ATTR || it.kind() == COMMENT) |
199 | { | 204 | { |
200 | let end = match start.next_sibling_or_token() { | 205 | let end = match &start.next_sibling_or_token() { |
201 | Some(el) if el.kind() == WHITESPACE => el, | 206 | Some(el) if el.kind() == WHITESPACE => el.clone(), |
202 | Some(_) | None => start, | 207 | Some(_) | None => start.clone(), |
203 | }; | 208 | }; |
204 | self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); | 209 | self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); |
205 | } | 210 | } |
@@ -210,18 +215,18 @@ impl AstEditor<ast::FnDef> { | |||
210 | pub fn set_body(&mut self, body: &ast::Block) { | 215 | pub fn set_body(&mut self, body: &ast::Block) { |
211 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); | 216 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); |
212 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() { | 217 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() { |
213 | old_body.syntax().into() | 218 | old_body.syntax().clone().into() |
214 | } else if let Some(semi) = self.ast().semicolon_token() { | 219 | } else if let Some(semi) = self.ast().semicolon_token() { |
215 | to_insert.push(tokens::single_space().into()); | 220 | to_insert.push(tokens::single_space().into()); |
216 | semi.into() | 221 | semi.into() |
217 | } else { | 222 | } else { |
218 | to_insert.push(tokens::single_space().into()); | 223 | to_insert.push(tokens::single_space().into()); |
219 | to_insert.push(body.syntax().into()); | 224 | to_insert.push(body.syntax().clone().into()); |
220 | self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter()); | 225 | self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter()); |
221 | return; | 226 | return; |
222 | }; | 227 | }; |
223 | to_insert.push(body.syntax().into()); | 228 | to_insert.push(body.syntax().clone().into()); |
224 | let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi); | 229 | let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi); |
225 | self.ast = self.replace_children(replace_range, to_insert.into_iter()) | 230 | self.ast = self.replace_children(replace_range, to_insert.into_iter()) |
226 | } | 231 | } |
227 | } | 232 | } |
@@ -231,15 +236,15 @@ pub struct AstBuilder<N: AstNode> { | |||
231 | } | 236 | } |
232 | 237 | ||
233 | impl AstBuilder<ast::NamedField> { | 238 | impl AstBuilder<ast::NamedField> { |
234 | pub fn from_name(name: &Name) -> TreeArc<ast::NamedField> { | 239 | pub fn from_name(name: &Name) -> ast::NamedField { |
235 | ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name)) | 240 | ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name)) |
236 | } | 241 | } |
237 | 242 | ||
238 | fn from_text(text: &str) -> TreeArc<ast::NamedField> { | 243 | fn from_text(text: &str) -> ast::NamedField { |
239 | ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text)) | 244 | ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text)) |
240 | } | 245 | } |
241 | 246 | ||
242 | pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> TreeArc<ast::NamedField> { | 247 | pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> ast::NamedField { |
243 | match expr { | 248 | match expr { |
244 | Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())), | 249 | Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())), |
245 | None => Self::from_text(&name.syntax().to_string()), | 250 | None => Self::from_text(&name.syntax().to_string()), |
@@ -248,36 +253,36 @@ impl AstBuilder<ast::NamedField> { | |||
248 | } | 253 | } |
249 | 254 | ||
250 | impl AstBuilder<ast::Block> { | 255 | impl AstBuilder<ast::Block> { |
251 | fn from_text(text: &str) -> TreeArc<ast::Block> { | 256 | fn from_text(text: &str) -> ast::Block { |
252 | ast_node_from_file_text(&format!("fn f() {}", text)) | 257 | ast_node_from_file_text(&format!("fn f() {}", text)) |
253 | } | 258 | } |
254 | 259 | ||
255 | pub fn single_expr(e: &ast::Expr) -> TreeArc<ast::Block> { | 260 | pub fn single_expr(e: &ast::Expr) -> ast::Block { |
256 | Self::from_text(&format!("{{ {} }}", e.syntax())) | 261 | Self::from_text(&format!("{{ {} }}", e.syntax())) |
257 | } | 262 | } |
258 | } | 263 | } |
259 | 264 | ||
260 | impl AstBuilder<ast::Expr> { | 265 | impl AstBuilder<ast::Expr> { |
261 | fn from_text(text: &str) -> TreeArc<ast::Expr> { | 266 | fn from_text(text: &str) -> ast::Expr { |
262 | ast_node_from_file_text(&format!("fn f() {{ {}; }}", text)) | 267 | ast_node_from_file_text(&format!("fn f() {{ {}; }}", text)) |
263 | } | 268 | } |
264 | 269 | ||
265 | pub fn unit() -> TreeArc<ast::Expr> { | 270 | pub fn unit() -> ast::Expr { |
266 | Self::from_text("()") | 271 | Self::from_text("()") |
267 | } | 272 | } |
268 | 273 | ||
269 | pub fn unimplemented() -> TreeArc<ast::Expr> { | 274 | pub fn unimplemented() -> ast::Expr { |
270 | Self::from_text("unimplemented!()") | 275 | Self::from_text("unimplemented!()") |
271 | } | 276 | } |
272 | } | 277 | } |
273 | 278 | ||
274 | impl AstBuilder<ast::NameRef> { | 279 | impl AstBuilder<ast::NameRef> { |
275 | pub fn new(text: &str) -> TreeArc<ast::NameRef> { | 280 | pub fn new(text: &str) -> ast::NameRef { |
276 | ast_node_from_file_text(&format!("fn f() {{ {}; }}", text)) | 281 | ast_node_from_file_text(&format!("fn f() {{ {}; }}", text)) |
277 | } | 282 | } |
278 | } | 283 | } |
279 | 284 | ||
280 | fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> { | 285 | fn ast_node_from_file_text<N: AstNode>(text: &str) -> N { |
281 | let parse = SourceFile::parse(text); | 286 | let parse = SourceFile::parse(text); |
282 | let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned(); | 287 | let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned(); |
283 | res | 288 | res |
@@ -285,47 +290,49 @@ fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> { | |||
285 | 290 | ||
286 | mod tokens { | 291 | mod tokens { |
287 | use once_cell::sync::Lazy; | 292 | use once_cell::sync::Lazy; |
288 | use ra_syntax::{AstNode, SourceFile, SyntaxKind::*, SyntaxToken, TreeArc, T}; | 293 | use ra_syntax::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T}; |
289 | 294 | ||
290 | static SOURCE_FILE: Lazy<TreeArc<SourceFile>> = | 295 | static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;")); |
291 | Lazy::new(|| SourceFile::parse(",\n; ;").tree().to_owned()); | ||
292 | 296 | ||
293 | pub(crate) fn comma() -> SyntaxToken<'static> { | 297 | pub(crate) fn comma() -> SyntaxToken { |
294 | SOURCE_FILE | 298 | SOURCE_FILE |
299 | .tree() | ||
295 | .syntax() | 300 | .syntax() |
296 | .descendants_with_tokens() | 301 | .descendants_with_tokens() |
297 | .filter_map(|it| it.as_token()) | 302 | .filter_map(|it| it.as_token().cloned()) |
298 | .find(|it| it.kind() == T![,]) | 303 | .find(|it| it.kind() == T![,]) |
299 | .unwrap() | 304 | .unwrap() |
300 | } | 305 | } |
301 | 306 | ||
302 | pub(crate) fn single_space() -> SyntaxToken<'static> { | 307 | pub(crate) fn single_space() -> SyntaxToken { |
303 | SOURCE_FILE | 308 | SOURCE_FILE |
309 | .tree() | ||
304 | .syntax() | 310 | .syntax() |
305 | .descendants_with_tokens() | 311 | .descendants_with_tokens() |
306 | .filter_map(|it| it.as_token()) | 312 | .filter_map(|it| it.as_token().cloned()) |
307 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ") | 313 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ") |
308 | .unwrap() | 314 | .unwrap() |
309 | } | 315 | } |
310 | 316 | ||
311 | #[allow(unused)] | 317 | #[allow(unused)] |
312 | pub(crate) fn single_newline() -> SyntaxToken<'static> { | 318 | pub(crate) fn single_newline() -> SyntaxToken { |
313 | SOURCE_FILE | 319 | SOURCE_FILE |
320 | .tree() | ||
314 | .syntax() | 321 | .syntax() |
315 | .descendants_with_tokens() | 322 | .descendants_with_tokens() |
316 | .filter_map(|it| it.as_token()) | 323 | .filter_map(|it| it.as_token().cloned()) |
317 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n") | 324 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n") |
318 | .unwrap() | 325 | .unwrap() |
319 | } | 326 | } |
320 | 327 | ||
321 | pub(crate) struct WsBuilder(TreeArc<SourceFile>); | 328 | pub(crate) struct WsBuilder(SourceFile); |
322 | 329 | ||
323 | impl WsBuilder { | 330 | impl WsBuilder { |
324 | pub(crate) fn new(text: &str) -> WsBuilder { | 331 | pub(crate) fn new(text: &str) -> WsBuilder { |
325 | WsBuilder(SourceFile::parse(text).ok().unwrap()) | 332 | WsBuilder(SourceFile::parse(text).ok().unwrap()) |
326 | } | 333 | } |
327 | pub(crate) fn ws(&self) -> SyntaxToken<'_> { | 334 | pub(crate) fn ws(&self) -> SyntaxToken { |
328 | self.0.syntax().first_child_or_token().unwrap().as_token().unwrap() | 335 | self.0.syntax().first_child_or_token().unwrap().as_token().cloned().unwrap() |
329 | } | 336 | } |
330 | } | 337 | } |
331 | 338 | ||