diff options
Diffstat (limited to 'crates/syntax/src/ast/edit.rs')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 493 |
1 files changed, 38 insertions, 455 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index cbc75f922..61952377f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -2,20 +2,16 @@ | |||
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{ | 4 | use std::{ |
5 | array, fmt, iter, | 5 | fmt, iter, |
6 | ops::{self, RangeInclusive}, | 6 | ops::{self, RangeInclusive}, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | algo::{self, SyntaxRewriter}, | 12 | algo, |
13 | ast::{ | 13 | ast::{self, make, AstNode}, |
14 | self, | 14 | ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind, |
15 | make::{self, tokens}, | ||
16 | AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, | ||
17 | }, | ||
18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, | ||
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 15 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
20 | SyntaxNode, SyntaxToken, T, | 16 | SyntaxNode, SyntaxToken, T, |
21 | }; | 17 | }; |
@@ -29,250 +25,6 @@ impl ast::BinExpr { | |||
29 | } | 25 | } |
30 | } | 26 | } |
31 | 27 | ||
32 | impl ast::Fn { | ||
33 | #[must_use] | ||
34 | pub fn with_body(&self, body: ast::BlockExpr) -> ast::Fn { | ||
35 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
36 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() { | ||
37 | old_body.syntax().clone().into() | ||
38 | } else if let Some(semi) = self.semicolon_token() { | ||
39 | to_insert.push(make::tokens::single_space().into()); | ||
40 | semi.into() | ||
41 | } else { | ||
42 | to_insert.push(make::tokens::single_space().into()); | ||
43 | to_insert.push(body.syntax().clone().into()); | ||
44 | return self.insert_children(InsertPosition::Last, to_insert); | ||
45 | }; | ||
46 | to_insert.push(body.syntax().clone().into()); | ||
47 | self.replace_children(single_node(old_body_or_semi), to_insert) | ||
48 | } | ||
49 | |||
50 | #[must_use] | ||
51 | pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn { | ||
52 | if let Some(old) = self.generic_param_list() { | ||
53 | return self.replace_descendant(old, generic_args); | ||
54 | } | ||
55 | |||
56 | let anchor = self.name().expect("The function must have a name").syntax().clone(); | ||
57 | |||
58 | let to_insert = [generic_args.syntax().clone().into()]; | ||
59 | self.insert_children(InsertPosition::After(anchor.into()), array::IntoIter::new(to_insert)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | fn make_multiline<N>(node: N) -> N | ||
64 | where | ||
65 | N: AstNode + Clone, | ||
66 | { | ||
67 | let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
68 | Some(it) => it, | ||
69 | None => return node, | ||
70 | }; | ||
71 | let sibling = match l_curly.next_sibling_or_token() { | ||
72 | Some(it) => it, | ||
73 | None => return node, | ||
74 | }; | ||
75 | let existing_ws = match sibling.as_token() { | ||
76 | None => None, | ||
77 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
78 | Some(ws) => { | ||
79 | if ws.text().contains('\n') { | ||
80 | return node; | ||
81 | } | ||
82 | Some(ws.clone()) | ||
83 | } | ||
84 | }; | ||
85 | |||
86 | let indent = leading_indent(node.syntax()).unwrap_or_default(); | ||
87 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
88 | let to_insert = iter::once(ws.ws().into()); | ||
89 | match existing_ws { | ||
90 | None => node.insert_children(InsertPosition::After(l_curly), to_insert), | ||
91 | Some(ws) => node.replace_children(single_node(ws), to_insert), | ||
92 | } | ||
93 | } | ||
94 | |||
95 | impl ast::Impl { | ||
96 | #[must_use] | ||
97 | pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl { | ||
98 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
99 | if let Some(old_items) = self.assoc_item_list() { | ||
100 | let to_replace: SyntaxElement = old_items.syntax().clone().into(); | ||
101 | to_insert.push(items.syntax().clone().into()); | ||
102 | self.replace_children(single_node(to_replace), to_insert) | ||
103 | } else { | ||
104 | to_insert.push(make::tokens::single_space().into()); | ||
105 | to_insert.push(items.syntax().clone().into()); | ||
106 | self.insert_children(InsertPosition::Last, to_insert) | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | impl ast::AssocItemList { | ||
112 | #[must_use] | ||
113 | pub fn append_items( | ||
114 | &self, | ||
115 | items: impl IntoIterator<Item = ast::AssocItem>, | ||
116 | ) -> ast::AssocItemList { | ||
117 | let mut res = self.clone(); | ||
118 | if !self.syntax().text().contains_char('\n') { | ||
119 | res = make_multiline(res); | ||
120 | } | ||
121 | items.into_iter().for_each(|it| res = res.append_item(it)); | ||
122 | res.fixup_trailing_whitespace().unwrap_or(res) | ||
123 | } | ||
124 | |||
125 | #[must_use] | ||
126 | pub fn append_item(&self, item: ast::AssocItem) -> ast::AssocItemList { | ||
127 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
128 | Some(it) => ( | ||
129 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
130 | InsertPosition::After(it.syntax().clone().into()), | ||
131 | "\n\n", | ||
132 | ), | ||
133 | None => match self.l_curly_token() { | ||
134 | Some(it) => ( | ||
135 | " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(), | ||
136 | InsertPosition::After(it.into()), | ||
137 | "\n", | ||
138 | ), | ||
139 | None => return self.clone(), | ||
140 | }, | ||
141 | }; | ||
142 | let ws = tokens::WsBuilder::new(&format!("{}{}", whitespace, indent)); | ||
143 | let to_insert: ArrayVec<SyntaxElement, 2> = | ||
144 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
145 | self.insert_children(position, to_insert) | ||
146 | } | ||
147 | |||
148 | /// Remove extra whitespace between last item and closing curly brace. | ||
149 | fn fixup_trailing_whitespace(&self) -> Option<ast::AssocItemList> { | ||
150 | let first_token_after_items = | ||
151 | self.assoc_items().last()?.syntax().next_sibling_or_token()?; | ||
152 | let last_token_before_curly = self.r_curly_token()?.prev_sibling_or_token()?; | ||
153 | if last_token_before_curly != first_token_after_items { | ||
154 | // there is something more between last item and | ||
155 | // right curly than just whitespace - bail out | ||
156 | return None; | ||
157 | } | ||
158 | let whitespace = | ||
159 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; | ||
160 | let text = whitespace.syntax().text(); | ||
161 | let newline = text.rfind('\n')?; | ||
162 | let keep = tokens::WsBuilder::new(&text[newline..]); | ||
163 | Some(self.replace_children( | ||
164 | first_token_after_items..=last_token_before_curly, | ||
165 | std::iter::once(keep.ws().into()), | ||
166 | )) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | impl ast::RecordExprFieldList { | ||
171 | #[must_use] | ||
172 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | ||
173 | self.insert_field(InsertPosition::Last, field) | ||
174 | } | ||
175 | |||
176 | #[must_use] | ||
177 | pub fn insert_field( | ||
178 | &self, | ||
179 | position: InsertPosition<&'_ ast::RecordExprField>, | ||
180 | field: &ast::RecordExprField, | ||
181 | ) -> ast::RecordExprFieldList { | ||
182 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
183 | let ws; | ||
184 | let space = if is_multiline { | ||
185 | ws = tokens::WsBuilder::new(&format!( | ||
186 | "\n{} ", | ||
187 | leading_indent(self.syntax()).unwrap_or_default() | ||
188 | )); | ||
189 | ws.ws() | ||
190 | } else { | ||
191 | tokens::single_space() | ||
192 | }; | ||
193 | |||
194 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
195 | to_insert.push(space.into()); | ||
196 | to_insert.push(field.syntax().clone().into()); | ||
197 | to_insert.push(make::token(T![,]).into()); | ||
198 | |||
199 | macro_rules! after_l_curly { | ||
200 | () => {{ | ||
201 | let anchor = match self.l_curly_token() { | ||
202 | Some(it) => it.into(), | ||
203 | None => return self.clone(), | ||
204 | }; | ||
205 | InsertPosition::After(anchor) | ||
206 | }}; | ||
207 | } | ||
208 | |||
209 | macro_rules! after_field { | ||
210 | ($anchor:expr) => { | ||
211 | if let Some(comma) = $anchor | ||
212 | .syntax() | ||
213 | .siblings_with_tokens(Direction::Next) | ||
214 | .find(|it| it.kind() == T![,]) | ||
215 | { | ||
216 | InsertPosition::After(comma) | ||
217 | } else { | ||
218 | to_insert.insert(0, make::token(T![,]).into()); | ||
219 | InsertPosition::After($anchor.syntax().clone().into()) | ||
220 | } | ||
221 | }; | ||
222 | } | ||
223 | |||
224 | let position = match position { | ||
225 | InsertPosition::First => after_l_curly!(), | ||
226 | InsertPosition::Last => { | ||
227 | if !is_multiline { | ||
228 | // don't insert comma before curly | ||
229 | to_insert.pop(); | ||
230 | } | ||
231 | match self.fields().last() { | ||
232 | Some(it) => after_field!(it), | ||
233 | None => after_l_curly!(), | ||
234 | } | ||
235 | } | ||
236 | InsertPosition::Before(anchor) => { | ||
237 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
238 | } | ||
239 | InsertPosition::After(anchor) => after_field!(anchor), | ||
240 | }; | ||
241 | |||
242 | self.insert_children(position, to_insert) | ||
243 | } | ||
244 | } | ||
245 | |||
246 | impl ast::TypeAlias { | ||
247 | #[must_use] | ||
248 | pub fn remove_bounds(&self) -> ast::TypeAlias { | ||
249 | let colon = match self.colon_token() { | ||
250 | Some(it) => it, | ||
251 | None => return self.clone(), | ||
252 | }; | ||
253 | let end = match self.type_bound_list() { | ||
254 | Some(it) => it.syntax().clone().into(), | ||
255 | None => colon.clone().into(), | ||
256 | }; | ||
257 | self.replace_children(colon.into()..=end, iter::empty()) | ||
258 | } | ||
259 | } | ||
260 | |||
261 | impl ast::TypeParam { | ||
262 | #[must_use] | ||
263 | pub fn remove_bounds(&self) -> ast::TypeParam { | ||
264 | let colon = match self.colon_token() { | ||
265 | Some(it) => it, | ||
266 | None => return self.clone(), | ||
267 | }; | ||
268 | let end = match self.type_bound_list() { | ||
269 | Some(it) => it.syntax().clone().into(), | ||
270 | None => colon.clone().into(), | ||
271 | }; | ||
272 | self.replace_children(colon.into()..=end, iter::empty()) | ||
273 | } | ||
274 | } | ||
275 | |||
276 | impl ast::Path { | 28 | impl ast::Path { |
277 | #[must_use] | 29 | #[must_use] |
278 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -313,33 +65,7 @@ impl ast::PathSegment { | |||
313 | } | 65 | } |
314 | } | 66 | } |
315 | 67 | ||
316 | impl ast::Use { | ||
317 | #[must_use] | ||
318 | pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::Use { | ||
319 | if let Some(old) = self.use_tree() { | ||
320 | return self.replace_descendant(old, use_tree); | ||
321 | } | ||
322 | self.clone() | ||
323 | } | ||
324 | } | ||
325 | |||
326 | impl ast::UseTree { | 68 | impl ast::UseTree { |
327 | #[must_use] | ||
328 | pub fn with_path(&self, path: ast::Path) -> ast::UseTree { | ||
329 | if let Some(old) = self.path() { | ||
330 | return self.replace_descendant(old, path); | ||
331 | } | ||
332 | self.clone() | ||
333 | } | ||
334 | |||
335 | #[must_use] | ||
336 | pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree { | ||
337 | if let Some(old) = self.use_tree_list() { | ||
338 | return self.replace_descendant(old, use_tree_list); | ||
339 | } | ||
340 | self.clone() | ||
341 | } | ||
342 | |||
343 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. | 69 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. |
344 | #[must_use] | 70 | #[must_use] |
345 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { | 71 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { |
@@ -376,134 +102,6 @@ impl ast::UseTree { | |||
376 | } | 102 | } |
377 | } | 103 | } |
378 | 104 | ||
379 | impl ast::MatchArmList { | ||
380 | #[must_use] | ||
381 | pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { | ||
382 | let mut res = self.clone(); | ||
383 | res = res.strip_if_only_whitespace(); | ||
384 | if !res.syntax().text().contains_char('\n') { | ||
385 | res = make_multiline(res); | ||
386 | } | ||
387 | items.into_iter().for_each(|it| res = res.append_arm(it)); | ||
388 | res | ||
389 | } | ||
390 | |||
391 | fn strip_if_only_whitespace(&self) -> ast::MatchArmList { | ||
392 | let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']); | ||
393 | iter.next(); // Eat the curly | ||
394 | let mut inner = iter.take_while(|it| it.kind() != T!['}']); | ||
395 | if !inner.clone().all(|it| it.kind() == WHITESPACE) { | ||
396 | return self.clone(); | ||
397 | } | ||
398 | let start = match inner.next() { | ||
399 | Some(s) => s, | ||
400 | None => return self.clone(), | ||
401 | }; | ||
402 | let end = match inner.last() { | ||
403 | Some(s) => s, | ||
404 | None => start.clone(), | ||
405 | }; | ||
406 | self.replace_children(start..=end, &mut iter::empty()) | ||
407 | } | ||
408 | |||
409 | #[must_use] | ||
410 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
411 | let placeholder = | ||
412 | self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
413 | if let Some(placeholder) = placeholder { | ||
414 | self.remove_arm(&placeholder) | ||
415 | } else { | ||
416 | self.clone() | ||
417 | } | ||
418 | } | ||
419 | |||
420 | #[must_use] | ||
421 | fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList { | ||
422 | let start = arm.syntax().clone(); | ||
423 | let end = if let Some(comma) = start | ||
424 | .siblings_with_tokens(Direction::Next) | ||
425 | .skip(1) | ||
426 | .find(|it| !it.kind().is_trivia()) | ||
427 | .filter(|it| it.kind() == T![,]) | ||
428 | { | ||
429 | comma | ||
430 | } else { | ||
431 | start.clone().into() | ||
432 | }; | ||
433 | self.replace_children(start.into()..=end, None) | ||
434 | } | ||
435 | |||
436 | #[must_use] | ||
437 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
438 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
439 | Some(t) => t, | ||
440 | None => return self.clone(), | ||
441 | }; | ||
442 | let position = InsertPosition::Before(r_curly); | ||
443 | let arm_ws = tokens::WsBuilder::new(" "); | ||
444 | let match_indent = &leading_indent(self.syntax()).unwrap_or_default(); | ||
445 | let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent)); | ||
446 | let to_insert: ArrayVec<SyntaxElement, 3> = | ||
447 | [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into(); | ||
448 | self.insert_children(position, to_insert) | ||
449 | } | ||
450 | } | ||
451 | |||
452 | impl ast::GenericParamList { | ||
453 | #[must_use] | ||
454 | pub fn append_params( | ||
455 | &self, | ||
456 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
457 | ) -> ast::GenericParamList { | ||
458 | let mut res = self.clone(); | ||
459 | params.into_iter().for_each(|it| res = res.append_param(it)); | ||
460 | res | ||
461 | } | ||
462 | |||
463 | #[must_use] | ||
464 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { | ||
465 | let space = tokens::single_space(); | ||
466 | |||
467 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
468 | if self.generic_params().next().is_some() { | ||
469 | to_insert.push(space.into()); | ||
470 | } | ||
471 | to_insert.push(item.syntax().clone().into()); | ||
472 | |||
473 | macro_rules! after_l_angle { | ||
474 | () => {{ | ||
475 | let anchor = match self.l_angle_token() { | ||
476 | Some(it) => it.into(), | ||
477 | None => return self.clone(), | ||
478 | }; | ||
479 | InsertPosition::After(anchor) | ||
480 | }}; | ||
481 | } | ||
482 | |||
483 | macro_rules! after_field { | ||
484 | ($anchor:expr) => { | ||
485 | if let Some(comma) = $anchor | ||
486 | .syntax() | ||
487 | .siblings_with_tokens(Direction::Next) | ||
488 | .find(|it| it.kind() == T![,]) | ||
489 | { | ||
490 | InsertPosition::After(comma) | ||
491 | } else { | ||
492 | to_insert.insert(0, make::token(T![,]).into()); | ||
493 | InsertPosition::After($anchor.syntax().clone().into()) | ||
494 | } | ||
495 | }; | ||
496 | } | ||
497 | |||
498 | let position = match self.generic_params().last() { | ||
499 | Some(it) => after_field!(it), | ||
500 | None => after_l_angle!(), | ||
501 | }; | ||
502 | |||
503 | self.insert_children(position, to_insert) | ||
504 | } | ||
505 | } | ||
506 | |||
507 | #[must_use] | 105 | #[must_use] |
508 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 106 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
509 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 107 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -554,6 +152,12 @@ impl ops::Add<u8> for IndentLevel { | |||
554 | } | 152 | } |
555 | 153 | ||
556 | impl IndentLevel { | 154 | impl IndentLevel { |
155 | pub fn single() -> IndentLevel { | ||
156 | IndentLevel(0) | ||
157 | } | ||
158 | pub fn is_zero(&self) -> bool { | ||
159 | self.0 == 0 | ||
160 | } | ||
557 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { | 161 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { |
558 | match element { | 162 | match element { |
559 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), | 163 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), |
@@ -588,54 +192,40 @@ impl IndentLevel { | |||
588 | /// ``` | 192 | /// ``` |
589 | /// if you indent the block, the `{` token would stay put. | 193 | /// if you indent the block, the `{` token would stay put. |
590 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 194 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
591 | let mut rewriter = SyntaxRewriter::default(); | 195 | let res = node.clone_subtree().clone_for_update(); |
592 | node.descendants_with_tokens() | 196 | let tokens = res.preorder_with_tokens().filter_map(|event| match event { |
593 | .filter_map(|el| el.into_token()) | 197 | rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), |
594 | .filter_map(ast::Whitespace::cast) | 198 | _ => None, |
595 | .filter(|ws| { | 199 | }); |
596 | let text = ws.syntax().text(); | 200 | for token in tokens { |
597 | text.contains('\n') | 201 | if let Some(ws) = ast::Whitespace::cast(token) { |
598 | }) | 202 | if ws.text().contains('\n') { |
599 | .for_each(|ws| { | 203 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self)); |
600 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); | 204 | ted::replace(ws.syntax(), &new_ws) |
601 | rewriter.replace(ws.syntax(), &new_ws) | 205 | } |
602 | }); | 206 | } |
603 | rewriter.rewrite(&node) | 207 | } |
208 | res.clone_subtree() | ||
604 | } | 209 | } |
605 | 210 | ||
606 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { | 211 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { |
607 | let mut rewriter = SyntaxRewriter::default(); | 212 | let res = node.clone_subtree().clone_for_update(); |
608 | node.descendants_with_tokens() | 213 | let tokens = res.preorder_with_tokens().filter_map(|event| match event { |
609 | .filter_map(|el| el.into_token()) | 214 | rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), |
610 | .filter_map(ast::Whitespace::cast) | 215 | _ => None, |
611 | .filter(|ws| { | 216 | }); |
612 | let text = ws.syntax().text(); | 217 | for token in tokens { |
613 | text.contains('\n') | 218 | if let Some(ws) = ast::Whitespace::cast(token) { |
614 | }) | 219 | if ws.text().contains('\n') { |
615 | .for_each(|ws| { | 220 | let new_ws = make::tokens::whitespace( |
616 | let new_ws = make::tokens::whitespace( | 221 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
617 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), | 222 | ); |
618 | ); | 223 | ted::replace(ws.syntax(), &new_ws) |
619 | rewriter.replace(ws.syntax(), &new_ws) | 224 | } |
620 | }); | ||
621 | rewriter.rewrite(&node) | ||
622 | } | ||
623 | } | ||
624 | |||
625 | // FIXME: replace usages with IndentLevel above | ||
626 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | ||
627 | for token in prev_tokens(node.first_token()?) { | ||
628 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | ||
629 | let ws_text = ws.text(); | ||
630 | if let Some(pos) = ws_text.rfind('\n') { | ||
631 | return Some(ws_text[pos + 1..].into()); | ||
632 | } | 225 | } |
633 | } | 226 | } |
634 | if token.text().contains('\n') { | 227 | res.clone_subtree() |
635 | break; | ||
636 | } | ||
637 | } | 228 | } |
638 | None | ||
639 | } | 229 | } |
640 | 230 | ||
641 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | 231 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { |
@@ -662,13 +252,6 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { | |||
662 | let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); | 252 | let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); |
663 | Self::cast(new_syntax).unwrap() | 253 | Self::cast(new_syntax).unwrap() |
664 | } | 254 | } |
665 | |||
666 | #[must_use] | ||
667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { | ||
668 | let mut rewriter = SyntaxRewriter::default(); | ||
669 | rewriter.replace(old.syntax(), new.syntax()); | ||
670 | rewriter.rewrite_ast(self) | ||
671 | } | ||
672 | fn indent_level(&self) -> IndentLevel { | 255 | fn indent_level(&self) -> IndentLevel { |
673 | IndentLevel::from_node(self.syntax()) | 256 | IndentLevel::from_node(self.syntax()) |
674 | } | 257 | } |