From a3a722de9ffaefdd0a46194e7ea2edac9754fd38 Mon Sep 17 00:00:00 2001 From: unexge Date: Fri, 15 Jan 2021 22:14:51 +0300 Subject: Add Unmerge Use assist --- crates/assists/src/handlers/unmerge_use.rs | 213 +++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 crates/assists/src/handlers/unmerge_use.rs (limited to 'crates/assists/src/handlers') diff --git a/crates/assists/src/handlers/unmerge_use.rs b/crates/assists/src/handlers/unmerge_use.rs new file mode 100644 index 000000000..d7dfe70d9 --- /dev/null +++ b/crates/assists/src/handlers/unmerge_use.rs @@ -0,0 +1,213 @@ +use syntax::{ + algo::SyntaxRewriter, + ast::{self, edit::AstNodeEdit, VisibilityOwner}, + AstNode, SyntaxKind, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; + +// Assist: unmerge_use +// +// Extracts single use item from use list. +// +// ``` +// use std::fmt::{Debug, Display$0}; +// ``` +// -> +// ``` +// use std::fmt::{Debug}; +// 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_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; + if tree_list.use_trees().count() < 2 { + return None; + } + + let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?; + let path = resolve_full_path(&tree)?; + + let new_use = ast::make::use_( + use_.visibility(), + ast::make::use_tree(path, None, 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()); + + let target = tree.syntax().text_range(); + acc.add( + AssistId("unmerge_use", AssistKind::RefactorRewrite), + "Unmerge use", + target, + |builder| { + builder.rewrite(rewriter); + }, + ) +} + +fn resolve_full_path(tree: &ast::UseTree) -> Option { + let mut paths = tree + .syntax() + .ancestors() + .take_while(|n| n.kind() != SyntaxKind::USE_KW) + .filter_map(ast::UseTree::cast) + .filter_map(|t| t.path()); + + let mut final_path = paths.next()?; + for path in paths { + final_path = ast::make::path_concat(path, final_path) + } + Some(final_path) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn skip_single_use_item() { + check_assist_not_applicable( + unmerge_use, + r" +use std::fmt::Debug$0; +", + ); + check_assist_not_applicable( + unmerge_use, + r" +use std::fmt::{Debug$0}; +", + ); + check_assist_not_applicable( + unmerge_use, + r" +use std::fmt::Debug as Dbg$0; +", + ); + } + + #[test] + fn skip_single_glob_import() { + check_assist_not_applicable( + unmerge_use, + r" +use std::fmt::*$0; +", + ); + } + + #[test] + fn unmerge_use_item() { + check_assist( + unmerge_use, + r" +use std::fmt::{Debug, Display$0}; +", + r" +use std::fmt::{Debug}; +use std::fmt::Display; +", + ); + + check_assist( + unmerge_use, + r" +use std::fmt::{Debug, format$0, Display}; +", + r" +use std::fmt::{Debug, Display}; +use std::fmt::format; +", + ); + } + + #[test] + fn unmerge_glob_import() { + check_assist( + unmerge_use, + r" +use std::fmt::{*$0, Display}; +", + r" +use std::fmt::{Display}; +use std::fmt::*; +", + ); + } + + #[test] + fn unmerge_renamed_use_item() { + check_assist( + unmerge_use, + r" +use std::fmt::{Debug, Display as Disp$0}; +", + r" +use std::fmt::{Debug}; +use std::fmt::Display as Disp; +", + ); + } + + #[test] + fn unmerge_indented_use_item() { + check_assist( + unmerge_use, + r" +mod format { + use std::fmt::{Debug, Display$0 as Disp, format}; +} +", + r" +mod format { + use std::fmt::{Debug, format}; + use std::fmt::Display as Disp; +} +", + ); + } + + #[test] + fn unmerge_nested_use_item() { + check_assist( + unmerge_use, + r" +use foo::bar::{baz::{qux$0, foobar}, barbaz}; +", + r" +use foo::bar::{baz::{foobar}, barbaz}; +use foo::bar::baz::qux; +", + ); + } + + #[test] + fn unmerge_use_item_with_visibility() { + check_assist( + unmerge_use, + r" +pub use std::fmt::{Debug, Display$0}; +", + r" +pub use std::fmt::{Debug}; +pub use std::fmt::Display; +", + ); + } +} -- cgit v1.2.3