aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-03-18 09:57:55 +0000
committerAleksey Kladov <[email protected]>2021-03-22 17:47:46 +0000
commit9cbf09ec4f24aa30af1d9855a909a6cfc67188f7 (patch)
tree20057e98e523440ebc3120e1e3c104b306a97baf
parentd834306e7cf85fe0b07e4979d2ff39bf4ef1f6be (diff)
rewrite merge use trees assist to use muatable syntax trees
changelog internal
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs49
-rw-r--r--crates/ide_assists/src/handlers/unmerge_use.rs30
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs4
-rw-r--r--crates/syntax/src/ast/edit.rs46
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs44
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ted.rs28
7 files changed, 118 insertions, 85 deletions
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 7bd7e1e36..cfc472a32 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -1,8 +1,5 @@
1use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehavior}; 1use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehavior};
2use syntax::{ 2use syntax::{algo::neighbor, ast, ted, AstNode};
3 algo::{neighbor, SyntaxRewriter},
4 ast, AstNode,
5};
6 3
7use crate::{ 4use crate::{
8 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
@@ -24,33 +21,29 @@ use crate::{
24// ``` 21// ```
25pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 22pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let tree: ast::UseTree = ctx.find_node_at_offset()?; 23 let tree: ast::UseTree = ctx.find_node_at_offset()?;
27 let mut rewriter = SyntaxRewriter::default(); 24 let original_parent = tree.syntax().ancestors().last()?;
25
26 let tree = tree.clone_for_update();
27 let new_parent = tree.syntax().ancestors().last()?;
28
28 let mut offset = ctx.offset(); 29 let mut offset = ctx.offset();
29 30
31 let mut imports = None;
32 let mut uses = None;
30 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { 33 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
31 let (merged, to_delete) = 34 let (merged, to_remove) =
32 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { 35 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| {
33 try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) 36 try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2))
34 })?; 37 })?;
35 38
36 rewriter.replace_ast(&use_item, &merged); 39 imports = Some((use_item, merged, to_remove));
37 rewriter += to_delete.remove();
38
39 if to_delete.syntax().text_range().end() < offset {
40 offset -= to_delete.syntax().text_range().len();
41 }
42 } else { 40 } else {
43 let (merged, to_delete) = 41 let (merged, to_remove) =
44 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { 42 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| {
45 try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) 43 try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree))
46 })?; 44 })?;
47 45
48 rewriter.replace_ast(&tree, &merged); 46 uses = Some((tree.clone(), merged, to_remove))
49 rewriter += to_delete.remove();
50
51 if to_delete.syntax().text_range().end() < offset {
52 offset -= to_delete.syntax().text_range().len();
53 }
54 }; 47 };
55 48
56 let target = tree.syntax().text_range(); 49 let target = tree.syntax().text_range();
@@ -59,7 +52,23 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
59 "Merge imports", 52 "Merge imports",
60 target, 53 target,
61 |builder| { 54 |builder| {
62 builder.rewrite(rewriter); 55 if let Some((to_replace, replacement, to_remove)) = imports {
56 if to_remove.syntax().text_range().end() < offset {
57 offset -= to_remove.syntax().text_range().len();
58 }
59 ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
60 to_remove.remove();
61 }
62
63 if let Some((to_replace, replacement, to_remove)) = uses {
64 if to_remove.syntax().text_range().end() < offset {
65 offset -= to_remove.syntax().text_range().len();
66 }
67 ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
68 to_remove.remove()
69 }
70
71 builder.replace(original_parent.text_range(), new_parent.to_string())
63 }, 72 },
64 ) 73 )
65} 74}
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 616af7c2e..8d271e056 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 algo::SyntaxRewriter, 2 ast::{self, VisibilityOwner},
3 ast::{self, edit::AstNodeEdit, VisibilityOwner}, 3 ted::{self, Position},
4 AstNode, SyntaxKind, 4 AstNode, SyntaxKind,
5}; 5};
6 6
@@ -22,7 +22,7 @@ use crate::{
22// use std::fmt::Display; 22// use std::fmt::Display;
23// ``` 23// ```
24pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let tree: ast::UseTree = ctx.find_node_at_offset()?; 25 let tree: ast::UseTree = ctx.find_node_at_offset::<ast::UseTree>()?.clone_for_update();
26 26
27 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; 27 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
28 if tree_list.use_trees().count() < 2 { 28 if tree_list.use_trees().count() < 2 {
@@ -33,6 +33,9 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
33 let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?; 33 let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
34 let path = resolve_full_path(&tree)?; 34 let path = resolve_full_path(&tree)?;
35 35
36 let old_parent_range = use_.syntax().parent()?.text_range();
37 let new_parent = use_.syntax().parent()?;
38
36 let target = tree.syntax().text_range(); 39 let target = tree.syntax().text_range();
37 acc.add( 40 acc.add(
38 AssistId("unmerge_use", AssistKind::RefactorRewrite), 41 AssistId("unmerge_use", AssistKind::RefactorRewrite),
@@ -47,20 +50,13 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 tree.rename(), 50 tree.rename(),
48 tree.star_token().is_some(), 51 tree.star_token().is_some(),
49 ), 52 ),
50 ); 53 )
51 54 .clone_for_update();
52 let mut rewriter = SyntaxRewriter::default(); 55
53 rewriter += tree.remove(); 56 tree.remove();
54 rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline()); 57 ted::insert(Position::after(use_.syntax()), new_use.syntax());
55 if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize { 58
56 rewriter.insert_after( 59 builder.replace(old_parent_range, new_parent.to_string());
57 use_.syntax(),
58 &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)),
59 );
60 }
61 rewriter.insert_after(use_.syntax(), new_use.syntax());
62
63 builder.rewrite(rewriter);
64 }, 60 },
65 ) 61 )
66} 62}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 37acf95f0..20c195f82 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -213,7 +213,7 @@ pub fn try_merge_imports(
213 let lhs_tree = lhs.use_tree()?; 213 let lhs_tree = lhs.use_tree()?;
214 let rhs_tree = rhs.use_tree()?; 214 let rhs_tree = rhs.use_tree()?;
215 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; 215 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
216 Some(lhs.with_use_tree(merged)) 216 Some(lhs.with_use_tree(merged).clone_for_update())
217} 217}
218 218
219pub fn try_merge_trees( 219pub fn try_merge_trees(
@@ -234,7 +234,7 @@ pub fn try_merge_trees(
234 } else { 234 } else {
235 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) 235 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
236 }; 236 };
237 recursive_merge(&lhs, &rhs, merge) 237 recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
238} 238}
239 239
240/// Recursively "zips" together lhs and rhs. 240/// Recursively "zips" together lhs and rhs.
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 347862b8a..18820786a 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -9,7 +9,7 @@ use std::{
9use arrayvec::ArrayVec; 9use arrayvec::ArrayVec;
10 10
11use crate::{ 11use crate::{
12 algo::{self, neighbor, SyntaxRewriter}, 12 algo::{self, SyntaxRewriter},
13 ast::{ 13 ast::{
14 self, 14 self,
15 make::{self, tokens}, 15 make::{self, tokens},
@@ -322,27 +322,6 @@ impl ast::Use {
322 } 322 }
323 self.clone() 323 self.clone()
324 } 324 }
325
326 pub fn remove(&self) -> SyntaxRewriter<'static> {
327 let mut res = SyntaxRewriter::default();
328 res.delete(self.syntax());
329 let next_ws = self
330 .syntax()
331 .next_sibling_or_token()
332 .and_then(|it| it.into_token())
333 .and_then(ast::Whitespace::cast);
334 if let Some(next_ws) = next_ws {
335 let ws_text = next_ws.syntax().text();
336 if let Some(rest) = ws_text.strip_prefix('\n') {
337 if rest.is_empty() {
338 res.delete(next_ws.syntax())
339 } else {
340 res.replace(next_ws.syntax(), &make::tokens::whitespace(rest));
341 }
342 }
343 }
344 res
345 }
346} 325}
347 326
348impl ast::UseTree { 327impl ast::UseTree {
@@ -396,22 +375,6 @@ impl ast::UseTree {
396 Some(res) 375 Some(res)
397 } 376 }
398 } 377 }
399
400 pub fn remove(&self) -> SyntaxRewriter<'static> {
401 let mut res = SyntaxRewriter::default();
402 res.delete(self.syntax());
403 for &dir in [Direction::Next, Direction::Prev].iter() {
404 if let Some(nb) = neighbor(self, dir) {
405 self.syntax()
406 .siblings_with_tokens(dir)
407 .skip(1)
408 .take_while(|it| it.as_node() != Some(nb.syntax()))
409 .for_each(|el| res.delete(&el));
410 return res;
411 }
412 }
413 res
414 }
415} 378}
416 379
417impl ast::MatchArmList { 380impl ast::MatchArmList {
@@ -592,6 +555,13 @@ impl ops::Add<u8> for IndentLevel {
592} 555}
593 556
594impl IndentLevel { 557impl IndentLevel {
558 pub fn from_element(element: &SyntaxElement) -> IndentLevel {
559 match element {
560 rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it),
561 rowan::NodeOrToken::Token(it) => IndentLevel::from_token(it),
562 }
563 }
564
595 pub fn from_node(node: &SyntaxNode) -> IndentLevel { 565 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
596 match node.first_token() { 566 match node.first_token() {
597 Some(it) => Self::from_token(&it), 567 Some(it) => Self::from_token(&it),
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index b1eed0a2c..529bd0eb1 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -2,13 +2,13 @@
2 2
3use std::iter::empty; 3use std::iter::empty;
4 4
5use ast::{edit::AstNodeEdit, make, GenericParamsOwner, WhereClause};
6use parser::T; 5use parser::T;
7 6
8use crate::{ 7use crate::{
9 ast, 8 algo::neighbor,
9 ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause},
10 ted::{self, Position}, 10 ted::{self, Position},
11 AstNode, Direction, 11 AstNode, AstToken, Direction,
12}; 12};
13 13
14use super::NameOwner; 14use super::NameOwner;
@@ -126,3 +126,41 @@ impl ast::TypeBoundList {
126 } 126 }
127 } 127 }
128} 128}
129
130impl ast::UseTree {
131 pub fn remove(&self) {
132 for &dir in [Direction::Next, Direction::Prev].iter() {
133 if let Some(next_use_tree) = neighbor(self, dir) {
134 let separators = self
135 .syntax()
136 .siblings_with_tokens(dir)
137 .skip(1)
138 .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
139 ted::remove_all_iter(separators);
140 break;
141 }
142 }
143 ted::remove(self.syntax())
144 }
145}
146
147impl ast::Use {
148 pub fn remove(&self) {
149 let next_ws = self
150 .syntax()
151 .next_sibling_or_token()
152 .and_then(|it| it.into_token())
153 .and_then(ast::Whitespace::cast);
154 if let Some(next_ws) = next_ws {
155 let ws_text = next_ws.syntax().text();
156 if let Some(rest) = ws_text.strip_prefix('\n') {
157 if rest.is_empty() {
158 ted::remove(next_ws.syntax())
159 } else {
160 ted::replace(next_ws.syntax(), make::tokens::whitespace(rest))
161 }
162 }
163 }
164 ted::remove(self.syntax())
165 }
166}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 7049affd9..c08f2c14f 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -560,7 +560,7 @@ pub mod tokens {
560 pub fn whitespace(text: &str) -> SyntaxToken { 560 pub fn whitespace(text: &str) -> SyntaxToken {
561 assert!(text.trim().is_empty()); 561 assert!(text.trim().is_empty());
562 let sf = SourceFile::parse(text).ok().unwrap(); 562 let sf = SourceFile::parse(text).ok().unwrap();
563 sf.syntax().first_child_or_token().unwrap().into_token().unwrap() 563 sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
564 } 564 }
565 565
566 pub fn doc_comment(text: &str) -> SyntaxToken { 566 pub fn doc_comment(text: &str) -> SyntaxToken {
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs
index be2b846b1..177d4ff67 100644
--- a/crates/syntax/src/ted.rs
+++ b/crates/syntax/src/ted.rs
@@ -2,11 +2,14 @@
2//! 2//!
3//! The `_raw`-suffixed functions insert elements as is, unsuffixed versions fix 3//! The `_raw`-suffixed functions insert elements as is, unsuffixed versions fix
4//! up elements around the edges. 4//! up elements around the edges.
5use std::ops::RangeInclusive; 5use std::{mem, ops::RangeInclusive};
6 6
7use parser::T; 7use parser::T;
8 8
9use crate::{ast::make, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken}; 9use crate::{
10 ast::{edit::IndentLevel, make},
11 SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken,
12};
10 13
11/// Utility trait to allow calling `ted` functions with references or owned 14/// Utility trait to allow calling `ted` functions with references or owned
12/// nodes. Do not use outside of this module. 15/// nodes. Do not use outside of this module.
@@ -101,12 +104,25 @@ pub fn insert_all_raw(position: Position, elements: Vec<SyntaxElement>) {
101} 104}
102 105
103pub fn remove(elem: impl Element) { 106pub fn remove(elem: impl Element) {
104 let elem = elem.syntax_element(); 107 elem.syntax_element().detach()
105 remove_all(elem.clone()..=elem)
106} 108}
107pub fn remove_all(range: RangeInclusive<SyntaxElement>) { 109pub fn remove_all(range: RangeInclusive<SyntaxElement>) {
108 replace_all(range, Vec::new()) 110 replace_all(range, Vec::new())
109} 111}
112pub fn remove_all_iter(range: impl IntoIterator<Item = SyntaxElement>) {
113 let mut it = range.into_iter();
114 if let Some(mut first) = it.next() {
115 match it.last() {
116 Some(mut last) => {
117 if first.index() > last.index() {
118 mem::swap(&mut first, &mut last)
119 }
120 remove_all(first..=last)
121 }
122 None => remove(first),
123 }
124 }
125}
110 126
111pub fn replace(old: impl Element, new: impl Element) { 127pub fn replace(old: impl Element, new: impl Element) {
112 let old = old.syntax_element(); 128 let old = old.syntax_element();
@@ -149,5 +165,9 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken
149 if right.kind() == T![;] || right.kind() == T![,] { 165 if right.kind() == T![;] || right.kind() == T![,] {
150 return None; 166 return None;
151 } 167 }
168 if right.kind() == SyntaxKind::USE {
169 let indent = IndentLevel::from_element(left);
170 return Some(make::tokens::whitespace(&format!("\n{}", indent)));
171 }
152 Some(make::tokens::single_space()) 172 Some(make::tokens::single_space())
153} 173}