aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r--crates/ide_assists/src/assist_context.rs49
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs35
-rw-r--r--crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs2
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs40
-rw-r--r--crates/ide_assists/src/lib.rs68
-rw-r--r--crates/ide_assists/src/tests.rs262
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs2
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};
20use text_edit::{TextEdit, TextEditBuilder}; 20use text_edit::{TextEdit, TextEditBuilder};
21 21
22use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel}; 22use 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
107pub(crate) struct Assists { 109pub(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
114impl Assists { 116impl 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
193pub(crate) struct TreeMutator {
194 immutable: SyntaxNode,
195 mutable_clone: SyntaxNode,
196}
197
198impl 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
191impl AssistBuilder { 215impl 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
23use crate::{ 24use 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
1368fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { 1369fn 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
1405fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { 1418fn 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#"
209trait Bar {
210 fn foo(&mut self) {}
211 fn fooo(&mut self) {}
212}
213
214struct Foo;
215impl Bar for Foo {
216 fn fooo(&mut self) {}
217 fn foo(&mut self) {$0}
218}"#,
219 r#"
220trait Bar {
221 fn foo(&mut self) {}
222 fn fooo(&mut self) {}
223}
224
225struct Foo;
226impl 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;
17pub mod utils; 17pub mod utils;
18pub mod ast_transform; 18pub mod ast_transform;
19 19
20use std::str::FromStr;
21
20use hir::Semantics; 22use hir::Semantics;
21use ide_db::base_db::FileRange; 23use ide_db::base_db::FileRange;
22use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; 24use 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
75impl 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)]
64pub struct AssistId(pub &'static str, pub AssistKind); 95pub 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)]
100pub 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)]
113pub 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
120impl 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)]
67pub struct GroupLabel(pub String); 133pub 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};
12use syntax::TextRange; 12use syntax::TextRange;
13use test_utils::{assert_eq_text, extract_offset}; 13use test_utils::{assert_eq_text, extract_offset};
14 14
15use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 15use crate::{
16 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy,
17 Assists, SingleResolve,
18};
16 19
17pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { 20pub(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]
282fn various_resolve_strategies() {
283 let (db, frange) = RootDatabase::with_range(
284 r#"
285pub 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