aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src/ast/edit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax/src/ast/edit.rs')
-rw-r--r--crates/syntax/src/ast/edit.rs296
1 files changed, 40 insertions, 256 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index cbc75f922..4b5f5c571 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -2,20 +2,20 @@
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.
4use std::{ 4use std::{
5 array, fmt, iter, 5 fmt, iter,
6 ops::{self, RangeInclusive}, 6 ops::{self, RangeInclusive},
7}; 7};
8 8
9use arrayvec::ArrayVec; 9use arrayvec::ArrayVec;
10 10
11use crate::{ 11use crate::{
12 algo::{self, SyntaxRewriter}, 12 algo,
13 ast::{ 13 ast::{
14 self, 14 self,
15 make::{self, tokens}, 15 make::{self, tokens},
16 AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, 16 AstNode,
17 }, 17 },
18 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, 18 ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind,
19 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 19 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
20 SyntaxNode, SyntaxToken, T, 20 SyntaxNode, SyntaxToken, T,
21}; 21};
@@ -29,37 +29,6 @@ impl ast::BinExpr {
29 } 29 }
30} 30}
31 31
32impl 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
63fn make_multiline<N>(node: N) -> N 32fn make_multiline<N>(node: N) -> N
64where 33where
65 N: AstNode + Clone, 34 N: AstNode + Clone,
@@ -92,81 +61,6 @@ where
92 } 61 }
93} 62}
94 63
95impl 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
111impl 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
170impl ast::RecordExprFieldList { 64impl ast::RecordExprFieldList {
171 #[must_use] 65 #[must_use]
172 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { 66 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
@@ -243,36 +137,6 @@ impl ast::RecordExprFieldList {
243 } 137 }
244} 138}
245 139
246impl 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
261impl 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
276impl ast::Path { 140impl ast::Path {
277 #[must_use] 141 #[must_use]
278 pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { 142 pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
@@ -313,33 +177,7 @@ impl ast::PathSegment {
313 } 177 }
314} 178}
315 179
316impl 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
326impl ast::UseTree { 180impl 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. 181 /// 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] 182 #[must_use]
345 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { 183 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
@@ -449,61 +287,6 @@ impl ast::MatchArmList {
449 } 287 }
450} 288}
451 289
452impl 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] 290#[must_use]
508pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 291pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
509 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 292 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
@@ -554,6 +337,12 @@ impl ops::Add<u8> for IndentLevel {
554} 337}
555 338
556impl IndentLevel { 339impl IndentLevel {
340 pub fn single() -> IndentLevel {
341 IndentLevel(0)
342 }
343 pub fn is_zero(&self) -> bool {
344 self.0 == 0
345 }
557 pub fn from_element(element: &SyntaxElement) -> IndentLevel { 346 pub fn from_element(element: &SyntaxElement) -> IndentLevel {
558 match element { 347 match element {
559 rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), 348 rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it),
@@ -588,37 +377,39 @@ impl IndentLevel {
588 /// ``` 377 /// ```
589 /// if you indent the block, the `{` token would stay put. 378 /// if you indent the block, the `{` token would stay put.
590 fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { 379 fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
591 let mut rewriter = SyntaxRewriter::default(); 380 let res = node.clone_subtree().clone_for_update();
592 node.descendants_with_tokens() 381 let tokens = res.preorder_with_tokens().filter_map(|event| match event {
593 .filter_map(|el| el.into_token()) 382 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
594 .filter_map(ast::Whitespace::cast) 383 _ => None,
595 .filter(|ws| { 384 });
596 let text = ws.syntax().text(); 385 for token in tokens {
597 text.contains('\n') 386 if let Some(ws) = ast::Whitespace::cast(token) {
598 }) 387 if ws.text().contains('\n') {
599 .for_each(|ws| { 388 let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self));
600 let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); 389 ted::replace(ws.syntax(), &new_ws)
601 rewriter.replace(ws.syntax(), &new_ws) 390 }
602 }); 391 }
603 rewriter.rewrite(&node) 392 }
393 res.clone_subtree()
604 } 394 }
605 395
606 fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { 396 fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
607 let mut rewriter = SyntaxRewriter::default(); 397 let res = node.clone_subtree().clone_for_update();
608 node.descendants_with_tokens() 398 let tokens = res.preorder_with_tokens().filter_map(|event| match event {
609 .filter_map(|el| el.into_token()) 399 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
610 .filter_map(ast::Whitespace::cast) 400 _ => None,
611 .filter(|ws| { 401 });
612 let text = ws.syntax().text(); 402 for token in tokens {
613 text.contains('\n') 403 if let Some(ws) = ast::Whitespace::cast(token) {
614 }) 404 if ws.text().contains('\n') {
615 .for_each(|ws| { 405 let new_ws = make::tokens::whitespace(
616 let new_ws = make::tokens::whitespace( 406 &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
617 &ws.syntax().text().replace(&format!("\n{}", self), "\n"), 407 );
618 ); 408 ted::replace(ws.syntax(), &new_ws)
619 rewriter.replace(ws.syntax(), &new_ws) 409 }
620 }); 410 }
621 rewriter.rewrite(&node) 411 }
412 res.clone_subtree()
622 } 413 }
623} 414}
624 415
@@ -662,13 +453,6 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
662 let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); 453 let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert);
663 Self::cast(new_syntax).unwrap() 454 Self::cast(new_syntax).unwrap()
664 } 455 }
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 { 456 fn indent_level(&self) -> IndentLevel {
673 IndentLevel::from_node(self.syntax()) 457 IndentLevel::from_node(self.syntax())
674 } 458 }