diff options
Diffstat (limited to 'crates/ide_assists')
-rw-r--r-- | crates/ide_assists/src/assist_context.rs | 49 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_function.rs | 35 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/reorder_impl.rs | 40 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 68 | ||||
-rw-r--r-- | crates/ide_assists/src/tests.rs | 262 | ||||
-rw-r--r-- | crates/ide_assists/src/utils/suggest_name.rs | 2 |
7 files changed, 414 insertions, 44 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 8714e4978..112939948 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -19,7 +19,9 @@ use syntax::{ | |||
19 | }; | 19 | }; |
20 | use text_edit::{TextEdit, TextEditBuilder}; | 20 | use text_edit::{TextEdit, TextEditBuilder}; |
21 | 21 | ||
22 | use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel}; | 22 | use crate::{ |
23 | assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, | ||
24 | }; | ||
23 | 25 | ||
24 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 26 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
25 | /// | 27 | /// |
@@ -105,14 +107,14 @@ impl<'a> AssistContext<'a> { | |||
105 | } | 107 | } |
106 | 108 | ||
107 | pub(crate) struct Assists { | 109 | pub(crate) struct Assists { |
108 | resolve: bool, | ||
109 | file: FileId, | 110 | file: FileId, |
111 | resolve: AssistResolveStrategy, | ||
110 | buf: Vec<Assist>, | 112 | buf: Vec<Assist>, |
111 | allowed: Option<Vec<AssistKind>>, | 113 | allowed: Option<Vec<AssistKind>>, |
112 | } | 114 | } |
113 | 115 | ||
114 | impl Assists { | 116 | impl Assists { |
115 | pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { | 117 | pub(crate) fn new(ctx: &AssistContext, resolve: AssistResolveStrategy) -> Assists { |
116 | Assists { | 118 | Assists { |
117 | resolve, | 119 | resolve, |
118 | file: ctx.frange.file_id, | 120 | file: ctx.frange.file_id, |
@@ -158,7 +160,7 @@ impl Assists { | |||
158 | } | 160 | } |
159 | 161 | ||
160 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 162 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
161 | let source_change = if self.resolve { | 163 | let source_change = if self.resolve.should_resolve(&assist.id) { |
162 | let mut builder = AssistBuilder::new(self.file); | 164 | let mut builder = AssistBuilder::new(self.file); |
163 | f(&mut builder); | 165 | f(&mut builder); |
164 | Some(builder.finish()) | 166 | Some(builder.finish()) |
@@ -185,7 +187,29 @@ pub(crate) struct AssistBuilder { | |||
185 | source_change: SourceChange, | 187 | source_change: SourceChange, |
186 | 188 | ||
187 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. | 189 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. |
188 | mutated_tree: Option<(SyntaxNode, SyntaxNode)>, | 190 | mutated_tree: Option<TreeMutator>, |
191 | } | ||
192 | |||
193 | pub(crate) struct TreeMutator { | ||
194 | immutable: SyntaxNode, | ||
195 | mutable_clone: SyntaxNode, | ||
196 | } | ||
197 | |||
198 | impl TreeMutator { | ||
199 | pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator { | ||
200 | let immutable = immutable.ancestors().last().unwrap(); | ||
201 | let mutable_clone = immutable.clone_for_update(); | ||
202 | TreeMutator { immutable, mutable_clone } | ||
203 | } | ||
204 | |||
205 | pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N { | ||
206 | N::cast(self.make_syntax_mut(node.syntax())).unwrap() | ||
207 | } | ||
208 | |||
209 | pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { | ||
210 | let ptr = SyntaxNodePtr::new(node); | ||
211 | ptr.to_node(&self.mutable_clone) | ||
212 | } | ||
189 | } | 213 | } |
190 | 214 | ||
191 | impl AssistBuilder { | 215 | impl AssistBuilder { |
@@ -204,8 +228,8 @@ impl AssistBuilder { | |||
204 | } | 228 | } |
205 | 229 | ||
206 | fn commit(&mut self) { | 230 | fn commit(&mut self) { |
207 | if let Some((old, new)) = self.mutated_tree.take() { | 231 | if let Some(tm) = self.mutated_tree.take() { |
208 | algo::diff(&old, &new).into_text_edit(&mut self.edit) | 232 | algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) |
209 | } | 233 | } |
210 | 234 | ||
211 | let edit = mem::take(&mut self.edit).finish(); | 235 | let edit = mem::take(&mut self.edit).finish(); |
@@ -228,16 +252,7 @@ impl AssistBuilder { | |||
228 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
229 | /// mutable state. | 253 | /// mutable state. |
230 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
231 | let root = &self | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
232 | .mutated_tree | ||
233 | .get_or_insert_with(|| { | ||
234 | let immutable = node.ancestors().last().unwrap(); | ||
235 | let mutable = immutable.clone_for_update(); | ||
236 | (immutable, mutable) | ||
237 | }) | ||
238 | .1; | ||
239 | let ptr = SyntaxNodePtr::new(&&node); | ||
240 | ptr.to_node(root) | ||
241 | } | 256 | } |
242 | 257 | ||
243 | /// Remove specified `range` of text. | 258 | /// Remove specified `range` of text. |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 5f80a40c8..93b28370c 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -16,12 +16,13 @@ use syntax::{ | |||
16 | edit::{AstNodeEdit, IndentLevel}, | 16 | edit::{AstNodeEdit, IndentLevel}, |
17 | AstNode, | 17 | AstNode, |
18 | }, | 18 | }, |
19 | ted, | ||
19 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
20 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
21 | }; | 22 | }; |
22 | 23 | ||
23 | use crate::{ | 24 | use crate::{ |
24 | assist_context::{AssistContext, Assists}, | 25 | assist_context::{AssistContext, Assists, TreeMutator}, |
25 | AssistId, | 26 | AssistId, |
26 | }; | 27 | }; |
27 | 28 | ||
@@ -1183,7 +1184,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1183 | } | 1184 | } |
1184 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { | 1185 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { |
1185 | let handler_ty = parent_ret_ty | 1186 | let handler_ty = parent_ret_ty |
1186 | .type_parameters() | 1187 | .type_arguments() |
1187 | .nth(1) | 1188 | .nth(1) |
1188 | .map(|ty| make_ty(&ty, ctx, module)) | 1189 | .map(|ty| make_ty(&ty, ctx, module)) |
1189 | .unwrap_or_else(make::ty_unit); | 1190 | .unwrap_or_else(make::ty_unit); |
@@ -1366,7 +1367,10 @@ fn rewrite_body_segment( | |||
1366 | 1367 | ||
1367 | /// change all usages to account for added `&`/`&mut` for some params | 1368 | /// change all usages to account for added `&`/`&mut` for some params |
1368 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { | 1369 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { |
1369 | let mut rewriter = SyntaxRewriter::default(); | 1370 | let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new(); |
1371 | |||
1372 | let tm = TreeMutator::new(syntax); | ||
1373 | |||
1370 | for param in params { | 1374 | for param in params { |
1371 | if !param.kind().is_ref() { | 1375 | if !param.kind().is_ref() { |
1372 | continue; | 1376 | continue; |
@@ -1376,30 +1380,39 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1376 | let usages = usages | 1380 | let usages = usages |
1377 | .iter() | 1381 | .iter() |
1378 | .filter(|reference| syntax.text_range().contains_range(reference.range)) | 1382 | .filter(|reference| syntax.text_range().contains_range(reference.range)) |
1379 | .filter_map(|reference| path_element_of_reference(syntax, reference)); | 1383 | .filter_map(|reference| path_element_of_reference(syntax, reference)) |
1380 | for path in usages { | 1384 | .map(|expr| tm.make_mut(&expr)); |
1381 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | 1385 | |
1386 | usages_for_param.push((param, usages.collect())); | ||
1387 | } | ||
1388 | |||
1389 | let res = tm.make_syntax_mut(syntax); | ||
1390 | |||
1391 | for (param, usages) in usages_for_param { | ||
1392 | for usage in usages { | ||
1393 | match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | ||
1382 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { | 1394 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { |
1383 | // do nothing | 1395 | // do nothing |
1384 | } | 1396 | } |
1385 | Some(ast::Expr::RefExpr(node)) | 1397 | Some(ast::Expr::RefExpr(node)) |
1386 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => | 1398 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => |
1387 | { | 1399 | { |
1388 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1400 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1389 | } | 1401 | } |
1390 | Some(ast::Expr::RefExpr(node)) | 1402 | Some(ast::Expr::RefExpr(node)) |
1391 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => | 1403 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => |
1392 | { | 1404 | { |
1393 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1405 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1394 | } | 1406 | } |
1395 | Some(_) | None => { | 1407 | Some(_) | None => { |
1396 | rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); | 1408 | let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); |
1409 | ted::replace(usage.syntax(), p.syntax()) | ||
1397 | } | 1410 | } |
1398 | }; | 1411 | } |
1399 | } | 1412 | } |
1400 | } | 1413 | } |
1401 | 1414 | ||
1402 | rewriter.rewrite(syntax) | 1415 | res |
1403 | } | 1416 | } |
1404 | 1417 | ||
1405 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { |
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs index c13c6eebe..ce6998d82 100644 --- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -91,7 +91,7 @@ fn existing_from_impl( | |||
91 | 91 | ||
92 | let enum_type = enum_.ty(sema.db); | 92 | let enum_type = enum_.ty(sema.db); |
93 | 93 | ||
94 | let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db); | 94 | let wrapped_type = variant.fields(sema.db).get(0)?.ty(sema.db); |
95 | 95 | ||
96 | if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { | 96 | if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { |
97 | Some(()) | 97 | Some(()) |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 72d889248..54a9a468e 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,9 +79,12 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | methods.into_iter().zip(sorted).for_each(|(old, new)| { | 82 | let methods = |
83 | ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax()) | 83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); |
84 | }); | 84 | methods |
85 | .into_iter() | ||
86 | .zip(sorted) | ||
87 | .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax())); | ||
85 | }, | 88 | }, |
86 | ) | 89 | ) |
87 | } | 90 | } |
@@ -160,7 +163,7 @@ $0impl Bar for Foo {} | |||
160 | } | 163 | } |
161 | 164 | ||
162 | #[test] | 165 | #[test] |
163 | fn reorder_impl_trait_methods() { | 166 | fn reorder_impl_trait_functions() { |
164 | check_assist( | 167 | check_assist( |
165 | reorder_impl, | 168 | reorder_impl, |
166 | r#" | 169 | r#" |
@@ -197,4 +200,33 @@ impl Bar for Foo { | |||
197 | "#, | 200 | "#, |
198 | ) | 201 | ) |
199 | } | 202 | } |
203 | |||
204 | #[test] | ||
205 | fn reorder_impl_trait_methods_uneven_ident_lengths() { | ||
206 | check_assist( | ||
207 | reorder_impl, | ||
208 | r#" | ||
209 | trait Bar { | ||
210 | fn foo(&mut self) {} | ||
211 | fn fooo(&mut self) {} | ||
212 | } | ||
213 | |||
214 | struct Foo; | ||
215 | impl Bar for Foo { | ||
216 | fn fooo(&mut self) {} | ||
217 | fn foo(&mut self) {$0} | ||
218 | }"#, | ||
219 | r#" | ||
220 | trait Bar { | ||
221 | fn foo(&mut self) {} | ||
222 | fn fooo(&mut self) {} | ||
223 | } | ||
224 | |||
225 | struct Foo; | ||
226 | impl Bar for Foo { | ||
227 | fn foo(&mut self) {} | ||
228 | fn fooo(&mut self) {} | ||
229 | }"#, | ||
230 | ) | ||
231 | } | ||
200 | } | 232 | } |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 88ae5c9a9..2e0c58504 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -17,6 +17,8 @@ mod tests; | |||
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod ast_transform; | 18 | pub mod ast_transform; |
19 | 19 | ||
20 | use std::str::FromStr; | ||
21 | |||
20 | use hir::Semantics; | 22 | use hir::Semantics; |
21 | use ide_db::base_db::FileRange; | 23 | use ide_db::base_db::FileRange; |
22 | use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; | 24 | use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; |
@@ -56,6 +58,35 @@ impl AssistKind { | |||
56 | _ => return false, | 58 | _ => return false, |
57 | } | 59 | } |
58 | } | 60 | } |
61 | |||
62 | pub fn name(&self) -> &str { | ||
63 | match self { | ||
64 | AssistKind::None => "None", | ||
65 | AssistKind::QuickFix => "QuickFix", | ||
66 | AssistKind::Generate => "Generate", | ||
67 | AssistKind::Refactor => "Refactor", | ||
68 | AssistKind::RefactorExtract => "RefactorExtract", | ||
69 | AssistKind::RefactorInline => "RefactorInline", | ||
70 | AssistKind::RefactorRewrite => "RefactorRewrite", | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | impl FromStr for AssistKind { | ||
76 | type Err = String; | ||
77 | |||
78 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
79 | match s { | ||
80 | "None" => Ok(AssistKind::None), | ||
81 | "QuickFix" => Ok(AssistKind::QuickFix), | ||
82 | "Generate" => Ok(AssistKind::Generate), | ||
83 | "Refactor" => Ok(AssistKind::Refactor), | ||
84 | "RefactorExtract" => Ok(AssistKind::RefactorExtract), | ||
85 | "RefactorInline" => Ok(AssistKind::RefactorInline), | ||
86 | "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), | ||
87 | unknown => Err(format!("Unknown AssistKind: '{}'", unknown)), | ||
88 | } | ||
89 | } | ||
59 | } | 90 | } |
60 | 91 | ||
61 | /// Unique identifier of the assist, should not be shown to the user | 92 | /// Unique identifier of the assist, should not be shown to the user |
@@ -63,6 +94,41 @@ impl AssistKind { | |||
63 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 94 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
64 | pub struct AssistId(pub &'static str, pub AssistKind); | 95 | pub struct AssistId(pub &'static str, pub AssistKind); |
65 | 96 | ||
97 | /// A way to control how many asssist to resolve during the assist resolution. | ||
98 | /// When an assist is resolved, its edits are calculated that might be costly to always do by default. | ||
99 | #[derive(Debug)] | ||
100 | pub enum AssistResolveStrategy { | ||
101 | /// No assists should be resolved. | ||
102 | None, | ||
103 | /// All assists should be resolved. | ||
104 | All, | ||
105 | /// Only a certain assist should be resolved. | ||
106 | Single(SingleResolve), | ||
107 | } | ||
108 | |||
109 | /// Hold the [`AssistId`] data of a certain assist to resolve. | ||
110 | /// The original id object cannot be used due to a `'static` lifetime | ||
111 | /// and the requirement to construct this struct dynamically during the resolve handling. | ||
112 | #[derive(Debug)] | ||
113 | pub struct SingleResolve { | ||
114 | /// The id of the assist. | ||
115 | pub assist_id: String, | ||
116 | // The kind of the assist. | ||
117 | pub assist_kind: AssistKind, | ||
118 | } | ||
119 | |||
120 | impl AssistResolveStrategy { | ||
121 | pub fn should_resolve(&self, id: &AssistId) -> bool { | ||
122 | match self { | ||
123 | AssistResolveStrategy::None => false, | ||
124 | AssistResolveStrategy::All => true, | ||
125 | AssistResolveStrategy::Single(single_resolve) => { | ||
126 | single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
66 | #[derive(Clone, Debug)] | 132 | #[derive(Clone, Debug)] |
67 | pub struct GroupLabel(pub String); | 133 | pub struct GroupLabel(pub String); |
68 | 134 | ||
@@ -91,7 +157,7 @@ impl Assist { | |||
91 | pub fn get( | 157 | pub fn get( |
92 | db: &RootDatabase, | 158 | db: &RootDatabase, |
93 | config: &AssistConfig, | 159 | config: &AssistConfig, |
94 | resolve: bool, | 160 | resolve: AssistResolveStrategy, |
95 | range: FileRange, | 161 | range: FileRange, |
96 | ) -> Vec<Assist> { | 162 | ) -> Vec<Assist> { |
97 | let sema = Semantics::new(db); | 163 | let sema = Semantics::new(db); |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 6f4f97361..9c2847998 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -12,7 +12,10 @@ use stdx::{format_to, trim_indent}; | |||
12 | use syntax::TextRange; | 12 | use syntax::TextRange; |
13 | use test_utils::{assert_eq_text, extract_offset}; | 13 | use test_utils::{assert_eq_text, extract_offset}; |
14 | 14 | ||
15 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; | 15 | use crate::{ |
16 | handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, | ||
17 | Assists, SingleResolve, | ||
18 | }; | ||
16 | 19 | ||
17 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | 20 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { |
18 | snippet_cap: SnippetCap::new(true), | 21 | snippet_cap: SnippetCap::new(true), |
@@ -65,14 +68,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
65 | let before = db.file_text(file_id).to_string(); | 68 | let before = db.file_text(file_id).to_string(); |
66 | let frange = FileRange { file_id, range: selection.into() }; | 69 | let frange = FileRange { file_id, range: selection.into() }; |
67 | 70 | ||
68 | let assist = Assist::get(&db, &TEST_CONFIG, true, frange) | 71 | let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) |
69 | .into_iter() | 72 | .into_iter() |
70 | .find(|assist| assist.id.0 == assist_id) | 73 | .find(|assist| assist.id.0 == assist_id) |
71 | .unwrap_or_else(|| { | 74 | .unwrap_or_else(|| { |
72 | panic!( | 75 | panic!( |
73 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 76 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
74 | assist_id, | 77 | assist_id, |
75 | Assist::get(&db, &TEST_CONFIG, false, frange) | 78 | Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) |
76 | .into_iter() | 79 | .into_iter() |
77 | .map(|assist| assist.id.0) | 80 | .map(|assist| assist.id.0) |
78 | .collect::<Vec<_>>() | 81 | .collect::<Vec<_>>() |
@@ -108,7 +111,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
108 | let sema = Semantics::new(&db); | 111 | let sema = Semantics::new(&db); |
109 | let config = TEST_CONFIG; | 112 | let config = TEST_CONFIG; |
110 | let ctx = AssistContext::new(sema, &config, frange); | 113 | let ctx = AssistContext::new(sema, &config, frange); |
111 | let mut acc = Assists::new(&ctx, true); | 114 | let mut acc = Assists::new(&ctx, AssistResolveStrategy::All); |
112 | handler(&mut acc, &ctx); | 115 | handler(&mut acc, &ctx); |
113 | let mut res = acc.finish(); | 116 | let mut res = acc.finish(); |
114 | 117 | ||
@@ -186,7 +189,7 @@ fn assist_order_field_struct() { | |||
186 | let (before_cursor_pos, before) = extract_offset(before); | 189 | let (before_cursor_pos, before) = extract_offset(before); |
187 | let (db, file_id) = with_single_file(&before); | 190 | let (db, file_id) = with_single_file(&before); |
188 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 191 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
189 | let assists = Assist::get(&db, &TEST_CONFIG, false, frange); | 192 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
190 | let mut assists = assists.iter(); | 193 | let mut assists = assists.iter(); |
191 | 194 | ||
192 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 195 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
@@ -211,7 +214,7 @@ pub fn test_some_range(a: int) -> bool { | |||
211 | "#, | 214 | "#, |
212 | ); | 215 | ); |
213 | 216 | ||
214 | let assists = Assist::get(&db, &TEST_CONFIG, false, frange); | 217 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
215 | let expected = labels(&assists); | 218 | let expected = labels(&assists); |
216 | 219 | ||
217 | expect![[r#" | 220 | expect![[r#" |
@@ -240,7 +243,7 @@ pub fn test_some_range(a: int) -> bool { | |||
240 | let mut cfg = TEST_CONFIG; | 243 | let mut cfg = TEST_CONFIG; |
241 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 244 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
242 | 245 | ||
243 | let assists = Assist::get(&db, &cfg, false, frange); | 246 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); |
244 | let expected = labels(&assists); | 247 | let expected = labels(&assists); |
245 | 248 | ||
246 | expect![[r#" | 249 | expect![[r#" |
@@ -255,7 +258,7 @@ pub fn test_some_range(a: int) -> bool { | |||
255 | { | 258 | { |
256 | let mut cfg = TEST_CONFIG; | 259 | let mut cfg = TEST_CONFIG; |
257 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 260 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
258 | let assists = Assist::get(&db, &cfg, false, frange); | 261 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); |
259 | let expected = labels(&assists); | 262 | let expected = labels(&assists); |
260 | 263 | ||
261 | expect![[r#" | 264 | expect![[r#" |
@@ -268,9 +271,250 @@ pub fn test_some_range(a: int) -> bool { | |||
268 | { | 271 | { |
269 | let mut cfg = TEST_CONFIG; | 272 | let mut cfg = TEST_CONFIG; |
270 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 273 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
271 | let assists = Assist::get(&db, &cfg, false, frange); | 274 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); |
272 | let expected = labels(&assists); | 275 | let expected = labels(&assists); |
273 | 276 | ||
274 | expect![[r#""#]].assert_eq(&expected); | 277 | expect![[r#""#]].assert_eq(&expected); |
275 | } | 278 | } |
276 | } | 279 | } |
280 | |||
281 | #[test] | ||
282 | fn various_resolve_strategies() { | ||
283 | let (db, frange) = RootDatabase::with_range( | ||
284 | r#" | ||
285 | pub fn test_some_range(a: int) -> bool { | ||
286 | if let 2..6 = $05$0 { | ||
287 | true | ||
288 | } else { | ||
289 | false | ||
290 | } | ||
291 | } | ||
292 | "#, | ||
293 | ); | ||
294 | |||
295 | let mut cfg = TEST_CONFIG; | ||
296 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | ||
297 | |||
298 | { | ||
299 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | ||
300 | assert_eq!(2, assists.len()); | ||
301 | let mut assists = assists.into_iter(); | ||
302 | |||
303 | let extract_into_variable_assist = assists.next().unwrap(); | ||
304 | expect![[r#" | ||
305 | Assist { | ||
306 | id: AssistId( | ||
307 | "extract_variable", | ||
308 | RefactorExtract, | ||
309 | ), | ||
310 | label: "Extract into variable", | ||
311 | group: None, | ||
312 | target: 59..60, | ||
313 | source_change: None, | ||
314 | } | ||
315 | "#]] | ||
316 | .assert_debug_eq(&extract_into_variable_assist); | ||
317 | |||
318 | let extract_into_function_assist = assists.next().unwrap(); | ||
319 | expect![[r#" | ||
320 | Assist { | ||
321 | id: AssistId( | ||
322 | "extract_function", | ||
323 | RefactorExtract, | ||
324 | ), | ||
325 | label: "Extract into function", | ||
326 | group: None, | ||
327 | target: 59..60, | ||
328 | source_change: None, | ||
329 | } | ||
330 | "#]] | ||
331 | .assert_debug_eq(&extract_into_function_assist); | ||
332 | } | ||
333 | |||
334 | { | ||
335 | let assists = Assist::get( | ||
336 | &db, | ||
337 | &cfg, | ||
338 | AssistResolveStrategy::Single(SingleResolve { | ||
339 | assist_id: "SOMETHING_MISMATCHING".to_string(), | ||
340 | assist_kind: AssistKind::RefactorExtract, | ||
341 | }), | ||
342 | frange, | ||
343 | ); | ||
344 | assert_eq!(2, assists.len()); | ||
345 | let mut assists = assists.into_iter(); | ||
346 | |||
347 | let extract_into_variable_assist = assists.next().unwrap(); | ||
348 | expect![[r#" | ||
349 | Assist { | ||
350 | id: AssistId( | ||
351 | "extract_variable", | ||
352 | RefactorExtract, | ||
353 | ), | ||
354 | label: "Extract into variable", | ||
355 | group: None, | ||
356 | target: 59..60, | ||
357 | source_change: None, | ||
358 | } | ||
359 | "#]] | ||
360 | .assert_debug_eq(&extract_into_variable_assist); | ||
361 | |||
362 | let extract_into_function_assist = assists.next().unwrap(); | ||
363 | expect![[r#" | ||
364 | Assist { | ||
365 | id: AssistId( | ||
366 | "extract_function", | ||
367 | RefactorExtract, | ||
368 | ), | ||
369 | label: "Extract into function", | ||
370 | group: None, | ||
371 | target: 59..60, | ||
372 | source_change: None, | ||
373 | } | ||
374 | "#]] | ||
375 | .assert_debug_eq(&extract_into_function_assist); | ||
376 | } | ||
377 | |||
378 | { | ||
379 | let assists = Assist::get( | ||
380 | &db, | ||
381 | &cfg, | ||
382 | AssistResolveStrategy::Single(SingleResolve { | ||
383 | assist_id: "extract_variable".to_string(), | ||
384 | assist_kind: AssistKind::RefactorExtract, | ||
385 | }), | ||
386 | frange, | ||
387 | ); | ||
388 | assert_eq!(2, assists.len()); | ||
389 | let mut assists = assists.into_iter(); | ||
390 | |||
391 | let extract_into_variable_assist = assists.next().unwrap(); | ||
392 | expect![[r#" | ||
393 | Assist { | ||
394 | id: AssistId( | ||
395 | "extract_variable", | ||
396 | RefactorExtract, | ||
397 | ), | ||
398 | label: "Extract into variable", | ||
399 | group: None, | ||
400 | target: 59..60, | ||
401 | source_change: Some( | ||
402 | SourceChange { | ||
403 | source_file_edits: { | ||
404 | FileId( | ||
405 | 0, | ||
406 | ): TextEdit { | ||
407 | indels: [ | ||
408 | Indel { | ||
409 | insert: "let $0var_name = 5;\n ", | ||
410 | delete: 45..45, | ||
411 | }, | ||
412 | Indel { | ||
413 | insert: "var_name", | ||
414 | delete: 59..60, | ||
415 | }, | ||
416 | ], | ||
417 | }, | ||
418 | }, | ||
419 | file_system_edits: [], | ||
420 | is_snippet: true, | ||
421 | }, | ||
422 | ), | ||
423 | } | ||
424 | "#]] | ||
425 | .assert_debug_eq(&extract_into_variable_assist); | ||
426 | |||
427 | let extract_into_function_assist = assists.next().unwrap(); | ||
428 | expect![[r#" | ||
429 | Assist { | ||
430 | id: AssistId( | ||
431 | "extract_function", | ||
432 | RefactorExtract, | ||
433 | ), | ||
434 | label: "Extract into function", | ||
435 | group: None, | ||
436 | target: 59..60, | ||
437 | source_change: None, | ||
438 | } | ||
439 | "#]] | ||
440 | .assert_debug_eq(&extract_into_function_assist); | ||
441 | } | ||
442 | |||
443 | { | ||
444 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange); | ||
445 | assert_eq!(2, assists.len()); | ||
446 | let mut assists = assists.into_iter(); | ||
447 | |||
448 | let extract_into_variable_assist = assists.next().unwrap(); | ||
449 | expect![[r#" | ||
450 | Assist { | ||
451 | id: AssistId( | ||
452 | "extract_variable", | ||
453 | RefactorExtract, | ||
454 | ), | ||
455 | label: "Extract into variable", | ||
456 | group: None, | ||
457 | target: 59..60, | ||
458 | source_change: Some( | ||
459 | SourceChange { | ||
460 | source_file_edits: { | ||
461 | FileId( | ||
462 | 0, | ||
463 | ): TextEdit { | ||
464 | indels: [ | ||
465 | Indel { | ||
466 | insert: "let $0var_name = 5;\n ", | ||
467 | delete: 45..45, | ||
468 | }, | ||
469 | Indel { | ||
470 | insert: "var_name", | ||
471 | delete: 59..60, | ||
472 | }, | ||
473 | ], | ||
474 | }, | ||
475 | }, | ||
476 | file_system_edits: [], | ||
477 | is_snippet: true, | ||
478 | }, | ||
479 | ), | ||
480 | } | ||
481 | "#]] | ||
482 | .assert_debug_eq(&extract_into_variable_assist); | ||
483 | |||
484 | let extract_into_function_assist = assists.next().unwrap(); | ||
485 | expect![[r#" | ||
486 | Assist { | ||
487 | id: AssistId( | ||
488 | "extract_function", | ||
489 | RefactorExtract, | ||
490 | ), | ||
491 | label: "Extract into function", | ||
492 | group: None, | ||
493 | target: 59..60, | ||
494 | source_change: Some( | ||
495 | SourceChange { | ||
496 | source_file_edits: { | ||
497 | FileId( | ||
498 | 0, | ||
499 | ): TextEdit { | ||
500 | indels: [ | ||
501 | Indel { | ||
502 | insert: "fun_name()", | ||
503 | delete: 59..60, | ||
504 | }, | ||
505 | Indel { | ||
506 | insert: "\n\nfn $0fun_name() -> i32 {\n 5\n}", | ||
507 | delete: 110..110, | ||
508 | }, | ||
509 | ], | ||
510 | }, | ||
511 | }, | ||
512 | file_system_edits: [], | ||
513 | is_snippet: true, | ||
514 | }, | ||
515 | ), | ||
516 | } | ||
517 | "#]] | ||
518 | .assert_debug_eq(&extract_into_function_assist); | ||
519 | } | ||
520 | } | ||
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index 533624c1f..deafcd630 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -227,7 +227,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> { | |||
227 | let name = adt.name(db).to_string(); | 227 | let name = adt.name(db).to_string(); |
228 | 228 | ||
229 | if WRAPPER_TYPES.contains(&name.as_str()) { | 229 | if WRAPPER_TYPES.contains(&name.as_str()) { |
230 | let inner_ty = ty.type_parameters().next()?; | 230 | let inner_ty = ty.type_arguments().next()?; |
231 | return name_of_type(&inner_ty, db); | 231 | return name_of_type(&inner_ty, db); |
232 | } | 232 | } |
233 | 233 | ||