use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehavior};
-use syntax::{
- algo::{neighbor, SyntaxRewriter},
- ast, AstNode,
-};
+use syntax::{algo::neighbor, ast, ted, AstNode};
use crate::{
assist_context::{AssistContext, Assists},
// ```
pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let tree: ast::UseTree = ctx.find_node_at_offset()?;
- let mut rewriter = SyntaxRewriter::default();
+ let original_parent = tree.syntax().ancestors().last()?;
+
+ let tree = tree.clone_for_update();
+ let new_parent = tree.syntax().ancestors().last()?;
+
let mut offset = ctx.offset();
+ let mut imports = None;
+ let mut uses = None;
if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
- let (merged, to_delete) =
+ let (merged, to_remove) =
next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| {
try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2))
})?;
- rewriter.replace_ast(&use_item, &merged);
- rewriter += to_delete.remove();
-
- if to_delete.syntax().text_range().end() < offset {
- offset -= to_delete.syntax().text_range().len();
- }
+ imports = Some((use_item, merged, to_remove));
} else {
- let (merged, to_delete) =
+ let (merged, to_remove) =
next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| {
try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree))
})?;
- rewriter.replace_ast(&tree, &merged);
- rewriter += to_delete.remove();
-
- if to_delete.syntax().text_range().end() < offset {
- offset -= to_delete.syntax().text_range().len();
- }
+ uses = Some((tree.clone(), merged, to_remove))
};
let target = tree.syntax().text_range();
"Merge imports",
target,
|builder| {
- builder.rewrite(rewriter);
+ if let Some((to_replace, replacement, to_remove)) = imports {
+ if to_remove.syntax().text_range().end() < offset {
+ offset -= to_remove.syntax().text_range().len();
+ }
+ ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
+ to_remove.remove();
+ }
+
+ if let Some((to_replace, replacement, to_remove)) = uses {
+ if to_remove.syntax().text_range().end() < offset {
+ offset -= to_remove.syntax().text_range().len();
+ }
+ ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
+ to_remove.remove()
+ }
+
+ builder.replace(original_parent.text_range(), new_parent.to_string())
},
)
}
use syntax::{
- algo::SyntaxRewriter,
- ast::{self, edit::AstNodeEdit, VisibilityOwner},
+ ast::{self, VisibilityOwner},
+ ted::{self, Position},
AstNode, SyntaxKind,
};
// use std::fmt::Display;
// ```
pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
- let tree: ast::UseTree = ctx.find_node_at_offset()?;
+ let tree: ast::UseTree = ctx.find_node_at_offset::<ast::UseTree>()?.clone_for_update();
let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
if tree_list.use_trees().count() < 2 {
let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
let path = resolve_full_path(&tree)?;
+ let old_parent_range = use_.syntax().parent()?.text_range();
+ let new_parent = use_.syntax().parent()?;
+
let target = tree.syntax().text_range();
acc.add(
AssistId("unmerge_use", AssistKind::RefactorRewrite),
tree.rename(),
tree.star_token().is_some(),
),
- );
-
- let mut rewriter = SyntaxRewriter::default();
- rewriter += tree.remove();
- rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline());
- if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize {
- rewriter.insert_after(
- use_.syntax(),
- &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)),
- );
- }
- rewriter.insert_after(use_.syntax(), new_use.syntax());
-
- builder.rewrite(rewriter);
+ )
+ .clone_for_update();
+
+ tree.remove();
+ ted::insert(Position::after(use_.syntax()), new_use.syntax());
+
+ builder.replace(old_parent_range, new_parent.to_string());
},
)
}
let lhs_tree = lhs.use_tree()?;
let rhs_tree = rhs.use_tree()?;
let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
- Some(lhs.with_use_tree(merged))
+ Some(lhs.with_use_tree(merged).clone_for_update())
}
pub fn try_merge_trees(
} else {
(lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
};
- recursive_merge(&lhs, &rhs, merge)
+ recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
}
/// Recursively "zips" together lhs and rhs.
use arrayvec::ArrayVec;
use crate::{
- algo::{self, neighbor, SyntaxRewriter},
+ algo::{self, SyntaxRewriter},
ast::{
self,
make::{self, tokens},
}
self.clone()
}
-
- pub fn remove(&self) -> SyntaxRewriter<'static> {
- let mut res = SyntaxRewriter::default();
- res.delete(self.syntax());
- let next_ws = self
- .syntax()
- .next_sibling_or_token()
- .and_then(|it| it.into_token())
- .and_then(ast::Whitespace::cast);
- if let Some(next_ws) = next_ws {
- let ws_text = next_ws.syntax().text();
- if let Some(rest) = ws_text.strip_prefix('\n') {
- if rest.is_empty() {
- res.delete(next_ws.syntax())
- } else {
- res.replace(next_ws.syntax(), &make::tokens::whitespace(rest));
- }
- }
- }
- res
- }
}
impl ast::UseTree {
Some(res)
}
}
-
- pub fn remove(&self) -> SyntaxRewriter<'static> {
- let mut res = SyntaxRewriter::default();
- res.delete(self.syntax());
- for &dir in [Direction::Next, Direction::Prev].iter() {
- if let Some(nb) = neighbor(self, dir) {
- self.syntax()
- .siblings_with_tokens(dir)
- .skip(1)
- .take_while(|it| it.as_node() != Some(nb.syntax()))
- .for_each(|el| res.delete(&el));
- return res;
- }
- }
- res
- }
}
impl ast::MatchArmList {
}
impl IndentLevel {
+ pub fn from_element(element: &SyntaxElement) -> IndentLevel {
+ match element {
+ rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it),
+ rowan::NodeOrToken::Token(it) => IndentLevel::from_token(it),
+ }
+ }
+
pub fn from_node(node: &SyntaxNode) -> IndentLevel {
match node.first_token() {
Some(it) => Self::from_token(&it),
use std::iter::empty;
-use ast::{edit::AstNodeEdit, make, GenericParamsOwner, WhereClause};
use parser::T;
use crate::{
- ast,
+ algo::neighbor,
+ ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause},
ted::{self, Position},
- AstNode, Direction,
+ AstNode, AstToken, Direction,
};
use super::NameOwner;
}
}
}
+
+impl ast::UseTree {
+ pub fn remove(&self) {
+ for &dir in [Direction::Next, Direction::Prev].iter() {
+ if let Some(next_use_tree) = neighbor(self, dir) {
+ let separators = self
+ .syntax()
+ .siblings_with_tokens(dir)
+ .skip(1)
+ .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
+ ted::remove_all_iter(separators);
+ break;
+ }
+ }
+ ted::remove(self.syntax())
+ }
+}
+
+impl ast::Use {
+ pub fn remove(&self) {
+ let next_ws = self
+ .syntax()
+ .next_sibling_or_token()
+ .and_then(|it| it.into_token())
+ .and_then(ast::Whitespace::cast);
+ if let Some(next_ws) = next_ws {
+ let ws_text = next_ws.syntax().text();
+ if let Some(rest) = ws_text.strip_prefix('\n') {
+ if rest.is_empty() {
+ ted::remove(next_ws.syntax())
+ } else {
+ ted::replace(next_ws.syntax(), make::tokens::whitespace(rest))
+ }
+ }
+ }
+ ted::remove(self.syntax())
+ }
+}
pub fn whitespace(text: &str) -> SyntaxToken {
assert!(text.trim().is_empty());
let sf = SourceFile::parse(text).ok().unwrap();
- sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
+ sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
}
pub fn doc_comment(text: &str) -> SyntaxToken {
//!
//! The `_raw`-suffixed functions insert elements as is, unsuffixed versions fix
//! up elements around the edges.
-use std::ops::RangeInclusive;
+use std::{mem, ops::RangeInclusive};
use parser::T;
-use crate::{ast::make, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken};
+use crate::{
+ ast::{edit::IndentLevel, make},
+ SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken,
+};
/// Utility trait to allow calling `ted` functions with references or owned
/// nodes. Do not use outside of this module.
}
pub fn remove(elem: impl Element) {
- let elem = elem.syntax_element();
- remove_all(elem.clone()..=elem)
+ elem.syntax_element().detach()
}
pub fn remove_all(range: RangeInclusive<SyntaxElement>) {
replace_all(range, Vec::new())
}
+pub fn remove_all_iter(range: impl IntoIterator<Item = SyntaxElement>) {
+ let mut it = range.into_iter();
+ if let Some(mut first) = it.next() {
+ match it.last() {
+ Some(mut last) => {
+ if first.index() > last.index() {
+ mem::swap(&mut first, &mut last)
+ }
+ remove_all(first..=last)
+ }
+ None => remove(first),
+ }
+ }
+}
pub fn replace(old: impl Element, new: impl Element) {
let old = old.syntax_element();
if right.kind() == T![;] || right.kind() == T![,] {
return None;
}
+ if right.kind() == SyntaxKind::USE {
+ let indent = IndentLevel::from_element(left);
+ return Some(make::tokens::whitespace(&format!("\n{}", indent)));
+ }
Some(make::tokens::single_space())
}