aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/auto_import.rs9
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs45
-rw-r--r--crates/assists/src/handlers/qualify_path.rs40
-rw-r--r--crates/assists/src/handlers/unmerge_use.rs231
4 files changed, 281 insertions, 44 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 55620f0f3..4e2a4fcd9 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,13 +1,11 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate},
2 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
3 mod_path_to_ast, 4 mod_path_to_ast,
4}; 5};
5use syntax::ast; 6use syntax::ast;
6 7
7use crate::{ 8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
8 utils::import_assets::{ImportAssets, ImportCandidate},
9 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
10};
11 9
12// Feature: Auto Import 10// Feature: Auto Import
13// 11//
@@ -121,8 +119,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
121 119
122fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
123 let name = match import_candidate { 121 let name = match import_candidate {
124 ImportCandidate::UnqualifiedName(candidate) 122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name),
125 | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name),
126 ImportCandidate::TraitAssocItem(candidate) => { 123 ImportCandidate::TraitAssocItem(candidate) => {
127 format!("Import a trait for item {}", &candidate.name) 124 format!("Import a trait for item {}", &candidate.name)
128 } 125 }
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index dc798daaa..0e63a60e8 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -79,29 +79,30 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
79 None => return Ok(false), 79 None => return Ok(false),
80 }; 80 };
81 81
82 Ok(!matches!((&initializer_expr, usage_parent), 82 Ok(!matches!(
83 (&initializer_expr, usage_parent),
83 (ast::Expr::CallExpr(_), _) 84 (ast::Expr::CallExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 85 | (ast::Expr::IndexExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 86 | (ast::Expr::MethodCallExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 87 | (ast::Expr::FieldExpr(_), _)
87 | (ast::Expr::TryExpr(_), _) 88 | (ast::Expr::TryExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 89 | (ast::Expr::RefExpr(_), _)
89 | (ast::Expr::Literal(_), _) 90 | (ast::Expr::Literal(_), _)
90 | (ast::Expr::TupleExpr(_), _) 91 | (ast::Expr::TupleExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 92 | (ast::Expr::ArrayExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 93 | (ast::Expr::ParenExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 94 | (ast::Expr::PathExpr(_), _)
94 | (ast::Expr::BlockExpr(_), _) 95 | (ast::Expr::BlockExpr(_), _)
95 | (ast::Expr::EffectExpr(_), _) 96 | (ast::Expr::EffectExpr(_), _)
96 | (_, ast::Expr::CallExpr(_)) 97 | (_, ast::Expr::CallExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 98 | (_, ast::Expr::TupleExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 99 | (_, ast::Expr::ArrayExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 100 | (_, ast::Expr::ParenExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 101 | (_, ast::Expr::ForExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 102 | (_, ast::Expr::WhileExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 103 | (_, ast::Expr::BreakExpr(_))
103 | (_, ast::Expr::ReturnExpr(_)) 104 | (_, ast::Expr::ReturnExpr(_))
104 | (_, ast::Expr::MatchExpr(_)) 105 | (_, ast::Expr::MatchExpr(_))
105 )) 106 ))
106 }) 107 })
107 .collect::<Result<Vec<_>, _>>()?; 108 .collect::<Result<Vec<_>, _>>()?;
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f7fbf37f4..a7d9fd4dc 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,7 +1,10 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::mod_path_to_ast; 4use ide_db::helpers::{
5 import_assets::{ImportAssets, ImportCandidate},
6 mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
@@ -12,7 +15,6 @@ use test_utils::mark;
12 15
13use crate::{ 16use crate::{
14 assist_context::{AssistContext, Assists}, 17 assist_context::{AssistContext, Assists},
15 utils::import_assets::{ImportAssets, ImportCandidate},
16 AssistId, AssistKind, GroupLabel, 18 AssistId, AssistKind, GroupLabel,
17}; 19};
18 20
@@ -53,17 +55,18 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
53 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 55 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
54 56
55 let qualify_candidate = match candidate { 57 let qualify_candidate = match candidate {
56 ImportCandidate::QualifierStart(_) => { 58 ImportCandidate::Path(candidate) => {
57 mark::hit!(qualify_path_qualifier_start); 59 if candidate.qualifier.is_some() {
58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 60 mark::hit!(qualify_path_qualifier_start);
59 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 61 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
60 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 62 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
61 } 63 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
62 ImportCandidate::UnqualifiedName(_) => { 64 } else {
63 mark::hit!(qualify_path_unqualified_name); 65 mark::hit!(qualify_path_unqualified_name);
64 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 66 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65 let generics = path.segment()?.generic_arg_list(); 67 let generics = path.segment()?.generic_arg_list();
66 QualifyCandidate::UnqualifiedName(generics) 68 QualifyCandidate::UnqualifiedName(generics)
69 }
67 } 70 }
68 ImportCandidate::TraitAssocItem(_) => { 71 ImportCandidate::TraitAssocItem(_) => {
69 mark::hit!(qualify_path_trait_assoc_item); 72 mark::hit!(qualify_path_trait_assoc_item);
@@ -186,7 +189,7 @@ fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> {
186 189
187fn group_label(candidate: &ImportCandidate) -> GroupLabel { 190fn group_label(candidate: &ImportCandidate) -> GroupLabel {
188 let name = match candidate { 191 let name = match candidate {
189 ImportCandidate::UnqualifiedName(it) | ImportCandidate::QualifierStart(it) => &it.name, 192 ImportCandidate::Path(it) => &it.name,
190 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 193 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
191 }; 194 };
192 GroupLabel(format!("Qualify {}", name)) 195 GroupLabel(format!("Qualify {}", name))
@@ -194,8 +197,13 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
194 197
195fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 198fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
196 match candidate { 199 match candidate {
197 ImportCandidate::UnqualifiedName(_) => format!("Qualify as `{}`", &import), 200 ImportCandidate::Path(candidate) => {
198 ImportCandidate::QualifierStart(_) => format!("Qualify with `{}`", &import), 201 if candidate.qualifier.is_some() {
202 format!("Qualify with `{}`", &import)
203 } else {
204 format!("Qualify as `{}`", &import)
205 }
206 }
199 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 207 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
200 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 208 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
201 } 209 }
diff --git a/crates/assists/src/handlers/unmerge_use.rs b/crates/assists/src/handlers/unmerge_use.rs
new file mode 100644
index 000000000..3dbef8e51
--- /dev/null
+++ b/crates/assists/src/handlers/unmerge_use.rs
@@ -0,0 +1,231 @@
1use syntax::{
2 algo::SyntaxRewriter,
3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind,
5};
6use test_utils::mark;
7
8use crate::{
9 assist_context::{AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13// Assist: unmerge_use
14//
15// Extracts single use item from use list.
16//
17// ```
18// use std::fmt::{Debug, Display$0};
19// ```
20// ->
21// ```
22// use std::fmt::{Debug};
23// use std::fmt::Display;
24// ```
25pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let tree: ast::UseTree = ctx.find_node_at_offset()?;
27
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item);
31 return None;
32 }
33
34 let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
35 let path = resolve_full_path(&tree)?;
36
37 let target = tree.syntax().text_range();
38 acc.add(
39 AssistId("unmerge_use", AssistKind::RefactorRewrite),
40 "Unmerge use",
41 target,
42 |builder| {
43 let new_use = ast::make::use_(
44 use_.visibility(),
45 ast::make::use_tree(
46 path,
47 tree.use_tree_list(),
48 tree.rename(),
49 tree.star_token().is_some(),
50 ),
51 );
52
53 let mut rewriter = SyntaxRewriter::default();
54 rewriter += tree.remove();
55 rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline());
56 if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize {
57 rewriter.insert_after(
58 use_.syntax(),
59 &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)),
60 );
61 }
62 rewriter.insert_after(use_.syntax(), new_use.syntax());
63
64 builder.rewrite(rewriter);
65 },
66 )
67}
68
69fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
70 let mut paths = tree
71 .syntax()
72 .ancestors()
73 .take_while(|n| n.kind() != SyntaxKind::USE_KW)
74 .filter_map(ast::UseTree::cast)
75 .filter_map(|t| t.path());
76
77 let mut final_path = paths.next()?;
78 for path in paths {
79 final_path = ast::make::path_concat(path, final_path)
80 }
81 Some(final_path)
82}
83
84#[cfg(test)]
85mod tests {
86 use crate::tests::{check_assist, check_assist_not_applicable};
87
88 use super::*;
89
90 #[test]
91 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item);
93 check_assist_not_applicable(
94 unmerge_use,
95 r"
96use std::fmt::Debug$0;
97",
98 );
99 check_assist_not_applicable(
100 unmerge_use,
101 r"
102use std::fmt::{Debug$0};
103",
104 );
105 check_assist_not_applicable(
106 unmerge_use,
107 r"
108use std::fmt::Debug as Dbg$0;
109",
110 );
111 }
112
113 #[test]
114 fn skip_single_glob_import() {
115 check_assist_not_applicable(
116 unmerge_use,
117 r"
118use std::fmt::*$0;
119",
120 );
121 }
122
123 #[test]
124 fn unmerge_use_item() {
125 check_assist(
126 unmerge_use,
127 r"
128use std::fmt::{Debug, Display$0};
129",
130 r"
131use std::fmt::{Debug};
132use std::fmt::Display;
133",
134 );
135
136 check_assist(
137 unmerge_use,
138 r"
139use std::fmt::{Debug, format$0, Display};
140",
141 r"
142use std::fmt::{Debug, Display};
143use std::fmt::format;
144",
145 );
146 }
147
148 #[test]
149 fn unmerge_glob_import() {
150 check_assist(
151 unmerge_use,
152 r"
153use std::fmt::{*$0, Display};
154",
155 r"
156use std::fmt::{Display};
157use std::fmt::*;
158",
159 );
160 }
161
162 #[test]
163 fn unmerge_renamed_use_item() {
164 check_assist(
165 unmerge_use,
166 r"
167use std::fmt::{Debug, Display as Disp$0};
168",
169 r"
170use std::fmt::{Debug};
171use std::fmt::Display as Disp;
172",
173 );
174 }
175
176 #[test]
177 fn unmerge_indented_use_item() {
178 check_assist(
179 unmerge_use,
180 r"
181mod format {
182 use std::fmt::{Debug, Display$0 as Disp, format};
183}
184",
185 r"
186mod format {
187 use std::fmt::{Debug, format};
188 use std::fmt::Display as Disp;
189}
190",
191 );
192 }
193
194 #[test]
195 fn unmerge_nested_use_item() {
196 check_assist(
197 unmerge_use,
198 r"
199use foo::bar::{baz::{qux$0, foobar}, barbaz};
200",
201 r"
202use foo::bar::{baz::{foobar}, barbaz};
203use foo::bar::baz::qux;
204",
205 );
206 check_assist(
207 unmerge_use,
208 r"
209use foo::bar::{baz$0::{qux, foobar}, barbaz};
210",
211 r"
212use foo::bar::{barbaz};
213use foo::bar::baz::{qux, foobar};
214",
215 );
216 }
217
218 #[test]
219 fn unmerge_use_item_with_visibility() {
220 check_assist(
221 unmerge_use,
222 r"
223pub use std::fmt::{Debug, Display$0};
224",
225 r"
226pub use std::fmt::{Debug};
227pub use std::fmt::Display;
228",
229 );
230 }
231}