diff options
Diffstat (limited to 'crates/ra_syntax/src/ast/edit.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 78 |
1 files changed, 53 insertions, 25 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 26e4576ff..29eb3fcb9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains functions for editing syntax trees. As the trees are | 1 | //! This module contains functions for editing syntax trees. As the trees are |
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::{iter, ops::RangeInclusive}; | 4 | use std::{ |
5 | fmt, iter, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
5 | 8 | ||
6 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
7 | 10 | ||
@@ -28,7 +31,7 @@ impl ast::BinExpr { | |||
28 | 31 | ||
29 | impl ast::FnDef { | 32 | impl ast::FnDef { |
30 | #[must_use] | 33 | #[must_use] |
31 | pub fn with_body(&self, body: ast::Block) -> ast::FnDef { | 34 | pub fn with_body(&self, body: ast::BlockExpr) -> ast::FnDef { |
32 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); | 35 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); |
33 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() { | 36 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() { |
34 | old_body.syntax().clone().into() | 37 | old_body.syntax().clone().into() |
@@ -79,7 +82,7 @@ where | |||
79 | 82 | ||
80 | impl ast::ItemList { | 83 | impl ast::ItemList { |
81 | #[must_use] | 84 | #[must_use] |
82 | pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList { | 85 | pub fn append_items(&self, items: impl IntoIterator<Item = ast::AssocItem>) -> ast::ItemList { |
83 | let mut res = self.clone(); | 86 | let mut res = self.clone(); |
84 | if !self.syntax().text().contains_char('\n') { | 87 | if !self.syntax().text().contains_char('\n') { |
85 | res = make_multiline(res); | 88 | res = make_multiline(res); |
@@ -89,8 +92,8 @@ impl ast::ItemList { | |||
89 | } | 92 | } |
90 | 93 | ||
91 | #[must_use] | 94 | #[must_use] |
92 | pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList { | 95 | pub fn append_item(&self, item: ast::AssocItem) -> ast::ItemList { |
93 | let (indent, position) = match self.impl_items().last() { | 96 | let (indent, position) = match self.assoc_items().last() { |
94 | Some(it) => ( | 97 | Some(it) => ( |
95 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | 98 | leading_indent(it.syntax()).unwrap_or_default().to_string(), |
96 | InsertPosition::After(it.syntax().clone().into()), | 99 | InsertPosition::After(it.syntax().clone().into()), |
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel { | |||
437 | } | 440 | } |
438 | } | 441 | } |
439 | 442 | ||
443 | impl fmt::Display for IndentLevel { | ||
444 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
445 | let spaces = " "; | ||
446 | let buf; | ||
447 | let len = self.0 as usize * 4; | ||
448 | let indent = if len <= spaces.len() { | ||
449 | &spaces[..len] | ||
450 | } else { | ||
451 | buf = iter::repeat(' ').take(len).collect::<String>(); | ||
452 | &buf | ||
453 | }; | ||
454 | fmt::Display::fmt(indent, f) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl ops::Add<u8> for IndentLevel { | ||
459 | type Output = IndentLevel; | ||
460 | fn add(self, rhs: u8) -> IndentLevel { | ||
461 | IndentLevel(self.0 + rhs) | ||
462 | } | ||
463 | } | ||
464 | |||
440 | impl IndentLevel { | 465 | impl IndentLevel { |
441 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 466 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
442 | let first_token = match node.first_token() { | 467 | let first_token = match node.first_token() { |
@@ -453,11 +478,15 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 478 | IndentLevel(0) |
454 | } | 479 | } |
455 | 480 | ||
456 | pub fn increase_indent<N: AstNode>(self, node: N) -> N { | 481 | /// XXX: this intentionally doesn't change the indent of the very first token. |
457 | N::cast(self._increase_indent(node.syntax().clone())).unwrap() | 482 | /// Ie, in something like |
458 | } | 483 | /// ``` |
459 | 484 | /// fn foo() { | |
460 | fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 485 | /// 92 |
486 | /// } | ||
487 | /// ``` | ||
488 | /// if you indent the block, the `{` token would stay put. | ||
489 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
461 | let mut rewriter = SyntaxRewriter::default(); | 490 | let mut rewriter = SyntaxRewriter::default(); |
462 | node.descendants_with_tokens() | 491 | node.descendants_with_tokens() |
463 | .filter_map(|el| el.into_token()) | 492 | .filter_map(|el| el.into_token()) |
@@ -467,22 +496,13 @@ impl IndentLevel { | |||
467 | text.contains('\n') | 496 | text.contains('\n') |
468 | }) | 497 | }) |
469 | .for_each(|ws| { | 498 | .for_each(|ws| { |
470 | let new_ws = make::tokens::whitespace(&format!( | 499 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); |
471 | "{}{:width$}", | ||
472 | ws.syntax().text(), | ||
473 | "", | ||
474 | width = self.0 as usize * 4 | ||
475 | )); | ||
476 | rewriter.replace(ws.syntax(), &new_ws) | 500 | rewriter.replace(ws.syntax(), &new_ws) |
477 | }); | 501 | }); |
478 | rewriter.rewrite(&node) | 502 | rewriter.rewrite(&node) |
479 | } | 503 | } |
480 | 504 | ||
481 | pub fn decrease_indent<N: AstNode>(self, node: N) -> N { | 505 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { |
482 | N::cast(self._decrease_indent(node.syntax().clone())).unwrap() | ||
483 | } | ||
484 | |||
485 | fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
486 | let mut rewriter = SyntaxRewriter::default(); | 506 | let mut rewriter = SyntaxRewriter::default(); |
487 | node.descendants_with_tokens() | 507 | node.descendants_with_tokens() |
488 | .filter_map(|el| el.into_token()) | 508 | .filter_map(|el| el.into_token()) |
@@ -493,7 +513,7 @@ impl IndentLevel { | |||
493 | }) | 513 | }) |
494 | .for_each(|ws| { | 514 | .for_each(|ws| { |
495 | let new_ws = make::tokens::whitespace( | 515 | let new_ws = make::tokens::whitespace( |
496 | &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), | 516 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
497 | ); | 517 | ); |
498 | rewriter.replace(ws.syntax(), &new_ws) | 518 | rewriter.replace(ws.syntax(), &new_ws) |
499 | }); | 519 | }); |
@@ -521,7 +541,7 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | |||
521 | iter::successors(Some(token), |token| token.prev_token()) | 541 | iter::successors(Some(token), |token| token.prev_token()) |
522 | } | 542 | } |
523 | 543 | ||
524 | pub trait AstNodeEdit: AstNode + Sized { | 544 | pub trait AstNodeEdit: AstNode + Clone + Sized { |
525 | #[must_use] | 545 | #[must_use] |
526 | fn insert_children( | 546 | fn insert_children( |
527 | &self, | 547 | &self, |
@@ -558,9 +578,17 @@ pub trait AstNodeEdit: AstNode + Sized { | |||
558 | } | 578 | } |
559 | rewriter.rewrite_ast(self) | 579 | rewriter.rewrite_ast(self) |
560 | } | 580 | } |
581 | #[must_use] | ||
582 | fn indent(&self, indent: IndentLevel) -> Self { | ||
583 | Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() | ||
584 | } | ||
585 | #[must_use] | ||
586 | fn dedent(&self, indent: IndentLevel) -> Self { | ||
587 | Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() | ||
588 | } | ||
561 | } | 589 | } |
562 | 590 | ||
563 | impl<N: AstNode> AstNodeEdit for N {} | 591 | impl<N: AstNode + Clone> AstNodeEdit for N {} |
564 | 592 | ||
565 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { | 593 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { |
566 | let element = element.into(); | 594 | let element = element.into(); |
@@ -580,7 +608,7 @@ fn test_increase_indent() { | |||
580 | _ => (), | 608 | _ => (), |
581 | }" | 609 | }" |
582 | ); | 610 | ); |
583 | let indented = IndentLevel(2).increase_indent(arm_list); | 611 | let indented = arm_list.indent(IndentLevel(2)); |
584 | assert_eq!( | 612 | assert_eq!( |
585 | indented.syntax().to_string(), | 613 | indented.syntax().to_string(), |
586 | "{ | 614 | "{ |