mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #7289
7289: Add Unmerge Use assist r=matklad a=unexge Closes https://github.com/rust-analyzer/rust-analyzer/issues/7185 Co-authored-by: unexge <unexge@gmail.com>
This commit is contained in:
commit
d93d3d6d73
5 changed files with 251 additions and 3 deletions
228
crates/assists/src/handlers/unmerge_use.rs
Normal file
228
crates/assists/src/handlers/unmerge_use.rs
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
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 target = tree.syntax().text_range();
|
||||||
|
acc.add(
|
||||||
|
AssistId("unmerge_use", AssistKind::RefactorRewrite),
|
||||||
|
"Unmerge use",
|
||||||
|
target,
|
||||||
|
|builder| {
|
||||||
|
let new_use = ast::make::use_(
|
||||||
|
use_.visibility(),
|
||||||
|
ast::make::use_tree(
|
||||||
|
path,
|
||||||
|
tree.use_tree_list(),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
|
||||||
|
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;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
unmerge_use,
|
||||||
|
r"
|
||||||
|
use foo::bar::{baz$0::{qux, foobar}, barbaz};
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use foo::bar::{barbaz};
|
||||||
|
use foo::bar::baz::{qux, foobar};
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -156,6 +156,7 @@ mod handlers {
|
||||||
mod replace_unwrap_with_match;
|
mod replace_unwrap_with_match;
|
||||||
mod split_import;
|
mod split_import;
|
||||||
mod toggle_ignore;
|
mod toggle_ignore;
|
||||||
|
mod unmerge_use;
|
||||||
mod unwrap_block;
|
mod unwrap_block;
|
||||||
mod wrap_return_type_in_result;
|
mod wrap_return_type_in_result;
|
||||||
|
|
||||||
|
@ -213,6 +214,7 @@ mod handlers {
|
||||||
replace_unwrap_with_match::replace_unwrap_with_match,
|
replace_unwrap_with_match::replace_unwrap_with_match,
|
||||||
split_import::split_import,
|
split_import::split_import,
|
||||||
toggle_ignore::toggle_ignore,
|
toggle_ignore::toggle_ignore,
|
||||||
|
unmerge_use::unmerge_use,
|
||||||
unwrap_block::unwrap_block,
|
unwrap_block::unwrap_block,
|
||||||
wrap_return_type_in_result::wrap_return_type_in_result,
|
wrap_return_type_in_result::wrap_return_type_in_result,
|
||||||
// These are manually sorted for better priorities
|
// These are manually sorted for better priorities
|
||||||
|
|
|
@ -1137,6 +1137,20 @@ fn arithmetics {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doctest_unmerge_use() {
|
||||||
|
check_doc_test(
|
||||||
|
"unmerge_use",
|
||||||
|
r#####"
|
||||||
|
use std::fmt::{Debug, Display$0};
|
||||||
|
"#####,
|
||||||
|
r#####"
|
||||||
|
use std::fmt::{Debug};
|
||||||
|
use std::fmt::Display;
|
||||||
|
"#####,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn doctest_unwrap_block() {
|
fn doctest_unwrap_block() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|
|
@ -97,7 +97,7 @@ pub fn insert_use<'a>(
|
||||||
) -> SyntaxRewriter<'a> {
|
) -> SyntaxRewriter<'a> {
|
||||||
let _p = profile::span("insert_use");
|
let _p = profile::span("insert_use");
|
||||||
let mut rewriter = SyntaxRewriter::default();
|
let mut rewriter = SyntaxRewriter::default();
|
||||||
let use_item = make::use_(make::use_tree(path.clone(), None, None, false));
|
let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
|
||||||
// merge into existing imports if possible
|
// merge into existing imports if possible
|
||||||
if let Some(mb) = merge {
|
if let Some(mb) = merge {
|
||||||
for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
|
for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
|
||||||
|
|
|
@ -108,8 +108,12 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::
|
||||||
ast_from_text(&format!("use {{{}}};", use_trees))
|
ast_from_text(&format!("use {{{}}};", use_trees))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_(use_tree: ast::UseTree) -> ast::Use {
|
pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
|
||||||
ast_from_text(&format!("use {};", use_tree))
|
let visibility = match visibility {
|
||||||
|
None => String::new(),
|
||||||
|
Some(it) => format!("{} ", it),
|
||||||
|
};
|
||||||
|
ast_from_text(&format!("{}use {};", visibility, use_tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
|
pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
|
||||||
|
|
Loading…
Reference in a new issue