aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs10
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs98
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs35
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs13
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs17
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs279
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs37
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs48
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs156
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs321
-rw-r--r--crates/ra_assists/src/handlers/extract_variable.rs (renamed from crates/ra_assists/src/handlers/introduce_variable.rs)270
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs257
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs208
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs21
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs15
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs15
-rw-r--r--crates/ra_assists/src/handlers/generate_derive.rs (renamed from crates/ra_assists/src/handlers/add_derive.rs)63
-rw-r--r--crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs (renamed from crates/ra_assists/src/handlers/add_from_impl_for_enum.rs)24
-rw-r--r--crates/ra_assists/src/handlers/generate_function.rs (renamed from crates/ra_assists/src/handlers/add_function.rs)91
-rw-r--r--crates/ra_assists/src/handlers/generate_impl.rs109
-rw-r--r--crates/ra_assists/src/handlers/generate_new.rs (renamed from crates/ra_assists/src/handlers/add_new.rs)46
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs24
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs (renamed from crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs)85
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs4
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs25
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs50
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs55
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs29
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs135
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs4
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs13
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs27
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs81
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs41
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs282
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs60
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs14
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs168
39 files changed, 2147 insertions, 1087 deletions
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index fa70c8496..acb07e36a 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -8,7 +8,7 @@ use stdx::SepBy;
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 AssistId, 11 AssistId, AssistKind,
12}; 12};
13 13
14// Assist: add_custom_impl 14// Assist: add_custom_impl
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); 52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
53 53
54 let target = attr.syntax().text_range(); 54 let target = attr.syntax().text_range();
55 acc.add(AssistId("add_custom_impl"), label, target, |builder| { 55 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
56 let new_attr_input = input 56 let new_attr_input = input
57 .syntax() 57 .syntax()
58 .descendants_with_tokens() 58 .descendants_with_tokens()
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index ab20c6649..39a5321d1 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextRange, 4 TextRange,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_explicit_type 9// Assist: add_explicit_type
10// 10//
@@ -57,9 +57,9 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
57 return None; 57 return None;
58 } 58 }
59 59
60 let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; 60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
61 acc.add( 61 acc.add(
62 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type", AssistKind::RefactorRewrite),
63 format!("Insert explicit type `{}`", inferred_type), 63 format!("Insert explicit type `{}`", inferred_type),
64 pat_range, 64 pat_range,
65 |builder| match ascribed_ty { 65 |builder| match ascribed_ty {
@@ -195,7 +195,7 @@ struct Test<K, T = u8> {
195} 195}
196 196
197fn main() { 197fn main() {
198 let test<|> = Test { t: 23, k: 33 }; 198 let test<|> = Test { t: 23u8, k: 33 };
199}"#, 199}"#,
200 r#" 200 r#"
201struct Test<K, T = u8> { 201struct Test<K, T = u8> {
@@ -204,7 +204,7 @@ struct Test<K, T = u8> {
204} 204}
205 205
206fn main() { 206fn main() {
207 let test: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23u8, k: 33 };
208}"#, 208}"#,
209 ); 209 );
210 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
deleted file mode 100644
index eceba7d0a..000000000
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, Assists};
5
6// Assist: add_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
30 let type_params = nominal.type_param_list();
31 let start_offset = nominal.syntax().text_range().end();
32 let mut buf = String::new();
33 buf.push_str("\n\nimpl");
34 if let Some(type_params) = &type_params {
35 format_to!(buf, "{}", type_params.syntax());
36 }
37 buf.push_str(" ");
38 buf.push_str(name.text().as_str());
39 if let Some(type_params) = type_params {
40 let lifetime_params = type_params
41 .lifetime_params()
42 .filter_map(|it| it.lifetime_token())
43 .map(|it| it.text().clone());
44 let type_params =
45 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
46
47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48 format_to!(buf, "<{}>", generic_params)
49 }
50 match ctx.config.snippet_cap {
51 Some(cap) => {
52 buf.push_str(" {\n $0\n}");
53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
60 })
61}
62
63#[cfg(test)]
64mod tests {
65 use crate::tests::{check_assist, check_assist_target};
66
67 use super::*;
68
69 #[test]
70 fn test_add_impl() {
71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
72 check_assist(
73 add_impl,
74 "struct Foo<T: Clone> {<|>}",
75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
76 );
77 check_assist(
78 add_impl,
79 "struct Foo<'a, T: Foo<'a>> {<|>}",
80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
81 );
82 }
83
84 #[test]
85 fn add_impl_target() {
86 check_assist_target(
87 add_impl,
88 "
89struct SomeThingIrrelevant;
90/// Has a lifetime parameter
91struct Foo<'a, T: Foo<'a>> {<|>}
92struct EvenMoreIrrelevant;
93",
94 "/// Has a lifetime parameter
95struct Foo<'a, T: Foo<'a>> {}",
96 );
97 }
98}
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index abacd4065..f185e61e5 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -12,7 +12,7 @@ use crate::{
12 assist_context::{AssistContext, Assists}, 12 assist_context::{AssistContext, Assists},
13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, 15 AssistId, AssistKind,
16}; 16};
17 17
18#[derive(PartialEq)] 18#[derive(PartialEq)]
@@ -128,9 +128,9 @@ fn add_missing_impl_members_inner(
128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) 128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
129 .iter() 129 .iter()
130 .map(|i| match i { 130 .map(|i| match i {
131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), 131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db()).value),
132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), 132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db()).value),
133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), 133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db()).value),
134 }) 134 })
135 .filter(|t| def_name(&t).is_some()) 135 .filter(|t| def_name(&t).is_some())
136 .filter(|t| match t { 136 .filter(|t| match t {
@@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
147 } 147 }
148 148
149 let target = impl_def.syntax().text_range(); 149 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id), label, target, |builder| { 150 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax()); 153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
@@ -158,6 +158,9 @@ fn add_missing_impl_members_inner(
158 .map(|it| ast_transform::apply(&*ast_transform, it)) 158 .map(|it| ast_transform::apply(&*ast_transform, it))
159 .map(|it| match it { 159 .map(|it| match it {
160 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), 160 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)),
161 ast::AssocItem::TypeAliasDef(def) => {
162 ast::AssocItem::TypeAliasDef(def.remove_bounds())
163 }
161 _ => it, 164 _ => it,
162 }) 165 })
163 .map(|it| edit::remove_attrs_and_docs(&it)); 166 .map(|it| edit::remove_attrs_and_docs(&it));
@@ -684,4 +687,26 @@ impl Foo<T> for S<T> {
684}"#, 687}"#,
685 ) 688 )
686 } 689 }
690
691 #[test]
692 fn test_assoc_type_bounds_are_removed() {
693 check_assist(
694 add_missing_impl_members,
695 r#"
696trait Tr {
697 type Ty: Copy + 'static;
698}
699
700impl Tr for ()<|> {
701}"#,
702 r#"
703trait Tr {
704 type Ty: Copy + 'static;
705}
706
707impl Tr for () {
708 $0type Ty;
709}"#,
710 )
711 }
687} 712}
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index 26acf81f2..f7e1a7b05 100644
--- a/crates/ra_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -4,7 +4,7 @@ use test_utils::mark;
4 4
5use crate::{ 5use crate::{
6 assist_context::{AssistContext, Assists}, 6 assist_context::{AssistContext, Assists},
7 AssistId, 7 AssistId, AssistKind,
8}; 8};
9 9
10// Assist: add_turbo_fish 10// Assist: add_turbo_fish
@@ -45,12 +45,15 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
45 mark::hit!(add_turbo_fish_non_generic); 45 mark::hit!(add_turbo_fish_non_generic);
46 return None; 46 return None;
47 } 47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { 48 acc.add(
49 match ctx.config.snippet_cap { 49 AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
50 "Add `::<>`",
51 ident.text_range(),
52 |builder| match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), 53 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"), 54 None => builder.insert(ident.text_range().end(), "::<_>"),
52 } 55 },
53 }) 56 )
54} 57}
55 58
56#[cfg(test)] 59#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 233e8fb8e..de701f8b8 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{self, AstNode}; 1use ra_syntax::ast::{self, AstNode};
2 2
3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: apply_demorgan 5// Assist: apply_demorgan
6// 6//
@@ -39,11 +39,16 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
39 let rhs_range = rhs.syntax().text_range(); 39 let rhs_range = rhs.syntax().text_range();
40 let not_rhs = invert_boolean_expression(rhs); 40 let not_rhs = invert_boolean_expression(rhs);
41 41
42 acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { 42 acc.add(
43 edit.replace(op_range, opposite_op); 43 AssistId("apply_demorgan", AssistKind::RefactorRewrite),
44 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 44 "Apply De Morgan's law",
45 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); 45 op_range,
46 }) 46 |edit| {
47 edit.replace(op_range, opposite_op);
48 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
49 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
50 },
51 )
47} 52}
48 53
49// Return the opposite text for a given logical operator, if it makes sense 54// Return the opposite text for a given logical operator, if it makes sense
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..947be3b9b 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -5,7 +5,7 @@ use hir::{
5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, 5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
6 Type, 6 Type,
7}; 7};
8use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; 8use ra_ide_db::{imports_locator, RootDatabase};
9use ra_prof::profile; 9use ra_prof::profile;
10use ra_syntax::{ 10use ra_syntax::{
11 ast::{self, AstNode}, 11 ast::{self, AstNode},
@@ -13,7 +13,9 @@ use ra_syntax::{
13}; 13};
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15 15
16use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; 16use crate::{
17 utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
18};
17 19
18// Assist: auto_import 20// Assist: auto_import
19// 21//
@@ -35,8 +37,8 @@ use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, Group
35// # pub mod std { pub mod collections { pub struct HashMap { } } } 37// # pub mod std { pub mod collections { pub struct HashMap { } } }
36// ``` 38// ```
37pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let auto_import_assets = AutoImportAssets::new(&ctx)?; 40 let auto_import_assets = AutoImportAssets::new(ctx)?;
39 let proposed_imports = auto_import_assets.search_for_imports(ctx.db); 41 let proposed_imports = auto_import_assets.search_for_imports(ctx);
40 if proposed_imports.is_empty() { 42 if proposed_imports.is_empty() {
41 return None; 43 return None;
42 } 44 }
@@ -46,7 +48,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
46 for import in proposed_imports { 48 for import in proposed_imports {
47 acc.add_group( 49 acc.add_group(
48 &group, 50 &group,
49 AssistId("auto_import"), 51 AssistId("auto_import", AssistKind::QuickFix),
50 format!("Import `{}`", &import), 52 format!("Import `{}`", &import),
51 range, 53 range,
52 |builder| { 54 |builder| {
@@ -127,11 +129,11 @@ impl AutoImportAssets {
127 GroupLabel(name) 129 GroupLabel(name)
128 } 130 }
129 131
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 132 fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 133 let _p = profile("auto_import::search_for_imports");
134 let db = ctx.db();
132 let current_crate = self.module_with_name_to_import.krate(); 135 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 136 imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query())
134 .find_imports(&self.get_search_query())
135 .into_iter() 137 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 138 .filter_map(|candidate| match &self.import_candidate {
137 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 139 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
@@ -488,16 +490,17 @@ mod tests {
488 check_assist( 490 check_assist(
489 auto_import, 491 auto_import,
490 r" 492 r"
491 //- /lib.rs crate:crate_with_macro 493//- /lib.rs crate:crate_with_macro
492 #[macro_export] 494#[macro_export]
493 macro_rules! foo { 495macro_rules! foo {
494 () => () 496 () => ()
495 } 497}
496 498
497 //- /main.rs crate:main deps:crate_with_macro 499//- /main.rs crate:main deps:crate_with_macro
498 fn main() { 500fn main() {
499 foo<|> 501 foo<|>
500 }", 502}
503",
501 r"use crate_with_macro::foo; 504 r"use crate_with_macro::foo;
502 505
503fn main() { 506fn main() {
@@ -810,6 +813,146 @@ fn main() {
810 } 813 }
811 814
812 #[test] 815 #[test]
816 fn trait_method_cross_crate() {
817 check_assist(
818 auto_import,
819 r"
820 //- /main.rs crate:main deps:dep
821 fn main() {
822 let test_struct = dep::test_mod::TestStruct {};
823 test_struct.test_meth<|>od()
824 }
825 //- /dep.rs crate:dep
826 pub mod test_mod {
827 pub trait TestTrait {
828 fn test_method(&self);
829 }
830 pub struct TestStruct {}
831 impl TestTrait for TestStruct {
832 fn test_method(&self) {}
833 }
834 }
835 ",
836 r"
837 use dep::test_mod::TestTrait;
838
839 fn main() {
840 let test_struct = dep::test_mod::TestStruct {};
841 test_struct.test_method()
842 }
843 ",
844 );
845 }
846
847 #[test]
848 fn assoc_fn_cross_crate() {
849 check_assist(
850 auto_import,
851 r"
852 //- /main.rs crate:main deps:dep
853 fn main() {
854 dep::test_mod::TestStruct::test_func<|>tion
855 }
856 //- /dep.rs crate:dep
857 pub mod test_mod {
858 pub trait TestTrait {
859 fn test_function();
860 }
861 pub struct TestStruct {}
862 impl TestTrait for TestStruct {
863 fn test_function() {}
864 }
865 }
866 ",
867 r"
868 use dep::test_mod::TestTrait;
869
870 fn main() {
871 dep::test_mod::TestStruct::test_function
872 }
873 ",
874 );
875 }
876
877 #[test]
878 fn assoc_const_cross_crate() {
879 check_assist(
880 auto_import,
881 r"
882 //- /main.rs crate:main deps:dep
883 fn main() {
884 dep::test_mod::TestStruct::CONST<|>
885 }
886 //- /dep.rs crate:dep
887 pub mod test_mod {
888 pub trait TestTrait {
889 const CONST: bool;
890 }
891 pub struct TestStruct {}
892 impl TestTrait for TestStruct {
893 const CONST: bool = true;
894 }
895 }
896 ",
897 r"
898 use dep::test_mod::TestTrait;
899
900 fn main() {
901 dep::test_mod::TestStruct::CONST
902 }
903 ",
904 );
905 }
906
907 #[test]
908 fn assoc_fn_as_method_cross_crate() {
909 check_assist_not_applicable(
910 auto_import,
911 r"
912 //- /main.rs crate:main deps:dep
913 fn main() {
914 let test_struct = dep::test_mod::TestStruct {};
915 test_struct.test_func<|>tion()
916 }
917 //- /dep.rs crate:dep
918 pub mod test_mod {
919 pub trait TestTrait {
920 fn test_function();
921 }
922 pub struct TestStruct {}
923 impl TestTrait for TestStruct {
924 fn test_function() {}
925 }
926 }
927 ",
928 );
929 }
930
931 #[test]
932 fn private_trait_cross_crate() {
933 check_assist_not_applicable(
934 auto_import,
935 r"
936 //- /main.rs crate:main deps:dep
937 fn main() {
938 let test_struct = dep::test_mod::TestStruct {};
939 test_struct.test_meth<|>od()
940 }
941 //- /dep.rs crate:dep
942 pub mod test_mod {
943 trait TestTrait {
944 fn test_method(&self);
945 }
946 pub struct TestStruct {}
947 impl TestTrait for TestStruct {
948 fn test_method(&self) {}
949 }
950 }
951 ",
952 );
953 }
954
955 #[test]
813 fn not_applicable_for_imported_trait_for_method() { 956 fn not_applicable_for_imported_trait_for_method() {
814 check_assist_not_applicable( 957 check_assist_not_applicable(
815 auto_import, 958 auto_import,
@@ -841,4 +984,106 @@ fn main() {
841 ", 984 ",
842 ) 985 )
843 } 986 }
987
988 #[test]
989 fn dep_import() {
990 check_assist(
991 auto_import,
992 r"
993//- /lib.rs crate:dep
994pub struct Struct;
995
996//- /main.rs crate:main deps:dep
997fn main() {
998 Struct<|>
999}
1000",
1001 r"use dep::Struct;
1002
1003fn main() {
1004 Struct
1005}
1006",
1007 );
1008 }
1009
1010 #[test]
1011 fn whole_segment() {
1012 // Tests that only imports whose last segment matches the identifier get suggested.
1013 check_assist(
1014 auto_import,
1015 r"
1016//- /lib.rs crate:dep
1017pub mod fmt {
1018 pub trait Display {}
1019}
1020
1021pub fn panic_fmt() {}
1022
1023//- /main.rs crate:main deps:dep
1024struct S;
1025
1026impl f<|>mt::Display for S {}
1027",
1028 r"use dep::fmt;
1029
1030struct S;
1031
1032impl fmt::Display for S {}
1033",
1034 );
1035 }
1036
1037 #[test]
1038 fn macro_generated() {
1039 // Tests that macro-generated items are suggested from external crates.
1040 check_assist(
1041 auto_import,
1042 r"
1043//- /lib.rs crate:dep
1044macro_rules! mac {
1045 () => {
1046 pub struct Cheese;
1047 };
1048}
1049
1050mac!();
1051
1052//- /main.rs crate:main deps:dep
1053fn main() {
1054 Cheese<|>;
1055}
1056",
1057 r"use dep::Cheese;
1058
1059fn main() {
1060 Cheese;
1061}
1062",
1063 );
1064 }
1065
1066 #[test]
1067 fn casing() {
1068 // Tests that differently cased names don't interfere and we only suggest the matching one.
1069 check_assist(
1070 auto_import,
1071 r"
1072//- /lib.rs crate:dep
1073pub struct FMT;
1074pub struct fmt;
1075
1076//- /main.rs crate:main deps:dep
1077fn main() {
1078 FMT<|>;
1079}
1080",
1081 r"use dep::FMT;
1082
1083fn main() {
1084 FMT;
1085}
1086",
1087 );
1088 }
844} 1089}
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
index c6baa0a57..24e5f6963 100644
--- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -3,7 +3,8 @@ use ra_syntax::{
3 AstNode, SyntaxNode, 3 AstNode, SyntaxNode,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7use test_utils::mark;
7 8
8// Assist: change_return_type_to_result 9// Assist: change_return_type_to_result
9// 10//
@@ -22,14 +23,19 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
22 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; 23 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
23 24
24 let type_ref = &ret_type.type_ref()?; 25 let type_ref = &ret_type.type_ref()?;
25 if type_ref.syntax().text().to_string().starts_with("Result<") { 26 let ret_type_str = type_ref.syntax().text().to_string();
26 return None; 27 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
28 if let Some(ret_type_first_part) = first_part_ret_type {
29 if ret_type_first_part.ends_with("Result") {
30 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
31 return None;
32 }
27 } 33 }
28 34
29 let block_expr = &fn_def.body()?; 35 let block_expr = &fn_def.body()?;
30 36
31 acc.add( 37 acc.add(
32 AssistId("change_return_type_to_result"), 38 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
33 "Change return type to Result", 39 "Change return type to Result",
34 type_ref.syntax().text_range(), 40 type_ref.syntax().text_range(),
35 |builder| { 41 |builder| {
@@ -297,6 +303,29 @@ mod tests {
297 } 303 }
298 304
299 #[test] 305 #[test]
306 fn change_return_type_to_result_simple_return_type_already_result_std() {
307 check_assist_not_applicable(
308 change_return_type_to_result,
309 r#"fn foo() -> std::result::Result<i32<|>, String> {
310 let test = "test";
311 return 42i32;
312 }"#,
313 );
314 }
315
316 #[test]
317 fn change_return_type_to_result_simple_return_type_already_result() {
318 mark::check!(change_return_type_to_result_simple_return_type_already_result);
319 check_assist_not_applicable(
320 change_return_type_to_result,
321 r#"fn foo() -> Result<i32<|>, String> {
322 let test = "test";
323 return 42i32;
324 }"#,
325 );
326 }
327
328 #[test]
300 fn change_return_type_to_result_simple_with_cursor() { 329 fn change_return_type_to_result_simple_with_cursor() {
301 check_assist( 330 check_assist(
302 change_return_type_to_result, 331 change_return_type_to_result,
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index c21d75be0..4343b423c 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -2,14 +2,13 @@ use ra_syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{ 4 SyntaxKind::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STATIC_DEF, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE,
7 }, 6 },
8 SyntaxNode, TextSize, T, 7 T,
9}; 8};
10use test_utils::mark; 9use test_utils::mark;
11 10
12use crate::{AssistContext, AssistId, Assists}; 11use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
13 12
14// Assist: change_visibility 13// Assist: change_visibility
15// 14//
@@ -30,14 +29,16 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
30} 29}
31 30
32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { 32 let item_keyword = ctx.token_at_offset().find(|leaf| {
34 T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, 33 matches!(
35 _ => false, 34 leaf.kind(),
35 T![const] | T![static] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait]
36 )
36 }); 37 });
37 38
38 let (offset, target) = if let Some(keyword) = item_keyword { 39 let (offset, target) = if let Some(keyword) = item_keyword {
39 let parent = keyword.parent(); 40 let parent = keyword.parent();
40 let def_kws = vec![CONST_DEF, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; 41 let def_kws = vec![CONST_DEF, STATIC_DEF, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
41 // Parent is not a definition, can't add visibility 42 // Parent is not a definition, can't add visibility
42 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { 43 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
43 return None; 44 return None;
@@ -66,27 +67,21 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
66 return None; 67 return None;
67 }; 68 };
68 69
69 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { 70 acc.add(
70 edit.insert(offset, "pub(crate) "); 71 AssistId("change_visibility", AssistKind::RefactorRewrite),
71 }) 72 "Change visibility to pub(crate)",
72} 73 target,
73 74 |edit| {
74fn vis_offset(node: &SyntaxNode) -> TextSize { 75 edit.insert(offset, "pub(crate) ");
75 node.children_with_tokens() 76 },
76 .skip_while(|it| match it.kind() { 77 )
77 WHITESPACE | COMMENT | ATTR => true,
78 _ => false,
79 })
80 .next()
81 .map(|it| it.text_range().start())
82 .unwrap_or_else(|| node.text_range().start())
83} 78}
84 79
85fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { 80fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
86 if vis.syntax().text() == "pub" { 81 if vis.syntax().text() == "pub" {
87 let target = vis.syntax().text_range(); 82 let target = vis.syntax().text_range();
88 return acc.add( 83 return acc.add(
89 AssistId("change_visibility"), 84 AssistId("change_visibility", AssistKind::RefactorRewrite),
90 "Change Visibility to pub(crate)", 85 "Change Visibility to pub(crate)",
91 target, 86 target,
92 |edit| { 87 |edit| {
@@ -97,7 +92,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
97 if vis.syntax().text() == "pub(crate)" { 92 if vis.syntax().text() == "pub(crate)" {
98 let target = vis.syntax().text_range(); 93 let target = vis.syntax().text_range();
99 return acc.add( 94 return acc.add(
100 AssistId("change_visibility"), 95 AssistId("change_visibility", AssistKind::RefactorRewrite),
101 "Change visibility to pub", 96 "Change visibility to pub",
102 target, 97 target,
103 |edit| { 98 |edit| {
@@ -162,6 +157,11 @@ mod tests {
162 } 157 }
163 158
164 #[test] 159 #[test]
160 fn change_visibility_static() {
161 check_assist(change_visibility, "<|>static FOO = 3u8;", "pub(crate) static FOO = 3u8;");
162 }
163
164 #[test]
165 fn change_visibility_handles_comment_attrs() { 165 fn change_visibility_handles_comment_attrs() {
166 check_assist( 166 check_assist(
167 change_visibility, 167 change_visibility,
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 4cc75a7ce..330459f3c 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -15,7 +15,7 @@ use ra_syntax::{
15use crate::{ 15use crate::{
16 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
17 utils::invert_boolean_expression, 17 utils::invert_boolean_expression,
18 AssistId, 18 AssistId, AssistKind,
19}; 19};
20 20
21// Assist: convert_to_guarded_return 21// Assist: convert_to_guarded_return
@@ -99,86 +99,92 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100 100
101 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(
103 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 103 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
104 let new_block = match if_let_pat { 104 "Convert to guarded return",
105 None => { 105 target,
106 // If. 106 |edit| {
107 let new_expr = { 107 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
108 let then_branch = 108 let new_block = match if_let_pat {
109 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 109 None => {
110 let cond = invert_boolean_expression(cond_expr); 110 // If.
111 make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) 111 let new_expr = {
112 }; 112 let then_branch =
113 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 113 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
114 } 114 let cond = invert_boolean_expression(cond_expr);
115 Some((path, bound_ident)) => { 115 make::expr_if(make::condition(cond, None), then_branch)
116 // If-let. 116 .indent(if_indent_level)
117 let match_expr = {
118 let happy_arm = {
119 let pat = make::tuple_struct_pat(
120 path,
121 once(make::bind_pat(make::name("it")).into()),
122 );
123 let expr = {
124 let name_ref = make::name_ref("it");
125 let segment = make::path_segment(name_ref);
126 let path = make::path_unqualified(segment);
127 make::expr_path(path)
128 };
129 make::match_arm(once(pat.into()), expr)
130 }; 117 };
118 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
119 }
120 Some((path, bound_ident)) => {
121 // If-let.
122 let match_expr = {
123 let happy_arm = {
124 let pat = make::tuple_struct_pat(
125 path,
126 once(make::bind_pat(make::name("it")).into()),
127 );
128 let expr = {
129 let name_ref = make::name_ref("it");
130 let segment = make::path_segment(name_ref);
131 let path = make::path_unqualified(segment);
132 make::expr_path(path)
133 };
134 make::match_arm(once(pat.into()), expr)
135 };
131 136
132 let sad_arm = make::match_arm( 137 let sad_arm = make::match_arm(
133 // FIXME: would be cool to use `None` or `Err(_)` if appropriate 138 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
134 once(make::placeholder_pat().into()), 139 once(make::placeholder_pat().into()),
135 early_expression, 140 early_expression,
136 ); 141 );
137 142
138 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 143 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
139 }; 144 };
140 145
141 let let_stmt = make::let_stmt( 146 let let_stmt = make::let_stmt(
142 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 147 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
143 Some(match_expr), 148 Some(match_expr),
149 );
150 let let_stmt = let_stmt.indent(if_indent_level);
151 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
152 }
153 };
154 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
155
156 fn replace(
157 new_expr: &SyntaxNode,
158 then_block: &ast::BlockExpr,
159 parent_block: &ast::BlockExpr,
160 if_expr: &ast::IfExpr,
161 ) -> SyntaxNode {
162 let then_block_items = then_block.dedent(IndentLevel(1));
163 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
164 let end_of_then =
165 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
166 end_of_then.prev_sibling_or_token().unwrap()
167 } else {
168 end_of_then
169 };
170 let mut then_statements = new_expr.children_with_tokens().chain(
171 then_block_items
172 .syntax()
173 .children_with_tokens()
174 .skip(1)
175 .take_while(|i| *i != end_of_then),
144 ); 176 );
145 let let_stmt = let_stmt.indent(if_indent_level); 177 replace_children(
146 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 178 &parent_block.syntax(),
179 RangeInclusive::new(
180 if_expr.clone().syntax().clone().into(),
181 if_expr.syntax().clone().into(),
182 ),
183 &mut then_statements,
184 )
147 } 185 }
148 }; 186 },
149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 187 )
150
151 fn replace(
152 new_expr: &SyntaxNode,
153 then_block: &ast::BlockExpr,
154 parent_block: &ast::BlockExpr,
155 if_expr: &ast::IfExpr,
156 ) -> SyntaxNode {
157 let then_block_items = then_block.dedent(IndentLevel::from(1));
158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
159 let end_of_then =
160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
161 end_of_then.prev_sibling_or_token().unwrap()
162 } else {
163 end_of_then
164 };
165 let mut then_statements = new_expr.children_with_tokens().chain(
166 then_block_items
167 .syntax()
168 .children_with_tokens()
169 .skip(1)
170 .take_while(|i| *i != end_of_then),
171 );
172 replace_children(
173 &parent_block.syntax(),
174 RangeInclusive::new(
175 if_expr.clone().syntax().clone().into(),
176 if_expr.syntax().clone().into(),
177 ),
178 &mut then_statements,
179 )
180 }
181 })
182} 188}
183 189
184#[cfg(test)] 190#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
new file mode 100644
index 000000000..2b8e273b3
--- /dev/null
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -0,0 +1,321 @@
1use hir::{EnumVariant, Module, ModuleDef, Name};
2use ra_db::FileId;
3use ra_fmt::leading_indent;
4use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
5use ra_syntax::{
6 algo::find_node_at_offset,
7 ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
8 SourceFile, SyntaxNode, TextRange, TextSize,
9};
10use rustc_hash::FxHashSet;
11
12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
14 AssistKind, Assists,
15};
16
17// Assist: extract_struct_from_enum_variant
18//
19// Extracts a struct from enum variant.
20//
21// ```
22// enum A { <|>One(u32, u32) }
23// ```
24// ->
25// ```
26// struct One(pub u32, pub u32);
27//
28// enum A { One(One) }
29// ```
30pub(crate) fn extract_struct_from_enum_variant(
31 acc: &mut Assists,
32 ctx: &AssistContext,
33) -> Option<()> {
34 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
35 let field_list = match variant.kind() {
36 ast::StructKind::Tuple(field_list) => field_list,
37 _ => return None,
38 };
39 let variant_name = variant.name()?.to_string();
40 let variant_hir = ctx.sema.to_def(&variant)?;
41 if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
42 return None;
43 }
44 let enum_ast = variant.parent_enum();
45 let visibility = enum_ast.visibility();
46 let enum_hir = ctx.sema.to_def(&enum_ast)?;
47 let variant_hir_name = variant_hir.name(ctx.db());
48 let enum_module_def = ModuleDef::from(enum_hir);
49 let current_module = enum_hir.module(ctx.db());
50 let target = variant.syntax().text_range();
51 acc.add(
52 AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
53 "Extract struct from enum variant",
54 target,
55 |builder| {
56 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
57 let res = definition.find_usages(&ctx.sema, None);
58 let start_offset = variant.parent_enum().syntax().text_range().start();
59 let mut visited_modules_set = FxHashSet::default();
60 visited_modules_set.insert(current_module);
61 for reference in res {
62 let source_file = ctx.sema.parse(reference.file_range.file_id);
63 update_reference(
64 ctx,
65 builder,
66 reference,
67 &source_file,
68 &enum_module_def,
69 &variant_hir_name,
70 &mut visited_modules_set,
71 );
72 }
73 extract_struct_def(
74 builder,
75 enum_ast.syntax(),
76 &variant_name,
77 &field_list.to_string(),
78 start_offset,
79 ctx.frange.file_id,
80 &visibility,
81 );
82 let list_range = field_list.syntax().text_range();
83 update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
84 },
85 )
86}
87
88fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
89 variant
90 .parent_enum(db)
91 .module(db)
92 .scope(db, None)
93 .into_iter()
94 .any(|(name, _)| name.to_string() == variant_name.to_string())
95}
96
97fn insert_import(
98 ctx: &AssistContext,
99 builder: &mut AssistBuilder,
100 path: &ast::PathExpr,
101 module: &Module,
102 enum_module_def: &ModuleDef,
103 variant_hir_name: &Name,
104) -> Option<()> {
105 let db = ctx.db();
106 let mod_path = module.find_use_path(db, enum_module_def.clone());
107 if let Some(mut mod_path) = mod_path {
108 mod_path.segments.pop();
109 mod_path.segments.push(variant_hir_name.clone());
110 insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
111 }
112 Some(())
113}
114
115fn extract_struct_def(
116 builder: &mut AssistBuilder,
117 enum_ast: &SyntaxNode,
118 variant_name: &str,
119 variant_list: &str,
120 start_offset: TextSize,
121 file_id: FileId,
122 visibility: &Option<ast::Visibility>,
123) -> Option<()> {
124 let visibility_string = if let Some(visibility) = visibility {
125 format!("{} ", visibility.to_string())
126 } else {
127 "".to_string()
128 };
129 let indent = if let Some(indent) = leading_indent(enum_ast) {
130 indent.to_string()
131 } else {
132 "".to_string()
133 };
134 let struct_def = format!(
135 r#"{}struct {}{};
136
137{}"#,
138 visibility_string,
139 variant_name,
140 list_with_visibility(variant_list),
141 indent
142 );
143 builder.edit_file(file_id);
144 builder.insert(start_offset, struct_def);
145 Some(())
146}
147
148fn update_variant(
149 builder: &mut AssistBuilder,
150 variant_name: &str,
151 file_id: FileId,
152 list_range: TextRange,
153) -> Option<()> {
154 let inside_variant_range = TextRange::new(
155 list_range.start().checked_add(TextSize::from(1))?,
156 list_range.end().checked_sub(TextSize::from(1))?,
157 );
158 builder.edit_file(file_id);
159 builder.replace(inside_variant_range, variant_name);
160 Some(())
161}
162
163fn update_reference(
164 ctx: &AssistContext,
165 builder: &mut AssistBuilder,
166 reference: Reference,
167 source_file: &SourceFile,
168 enum_module_def: &ModuleDef,
169 variant_hir_name: &Name,
170 visited_modules_set: &mut FxHashSet<Module>,
171) -> Option<()> {
172 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
173 source_file.syntax(),
174 reference.file_range.range.start(),
175 )?;
176 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
177 let list = call.arg_list()?;
178 let segment = path_expr.path()?.segment()?;
179 let module = ctx.sema.scope(&path_expr.syntax()).module()?;
180 let list_range = list.syntax().text_range();
181 let inside_list_range = TextRange::new(
182 list_range.start().checked_add(TextSize::from(1))?,
183 list_range.end().checked_sub(TextSize::from(1))?,
184 );
185 builder.edit_file(reference.file_range.file_id);
186 if !visited_modules_set.contains(&module) {
187 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
188 .is_some()
189 {
190 visited_modules_set.insert(module);
191 }
192 }
193 builder.replace(inside_list_range, format!("{}{}", segment, list));
194 Some(())
195}
196
197fn list_with_visibility(list: &str) -> String {
198 list.split(',')
199 .map(|part| {
200 let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
201 let mut mod_part = part.trim().to_string();
202 mod_part.insert_str(index, "pub ");
203 mod_part
204 })
205 .collect::<Vec<String>>()
206 .join(", ")
207}
208
209#[cfg(test)]
210mod tests {
211
212 use crate::{
213 tests::{check_assist, check_assist_not_applicable},
214 utils::FamousDefs,
215 };
216
217 use super::*;
218
219 #[test]
220 fn test_extract_struct_several_fields() {
221 check_assist(
222 extract_struct_from_enum_variant,
223 "enum A { <|>One(u32, u32) }",
224 r#"struct One(pub u32, pub u32);
225
226enum A { One(One) }"#,
227 );
228 }
229
230 #[test]
231 fn test_extract_struct_one_field() {
232 check_assist(
233 extract_struct_from_enum_variant,
234 "enum A { <|>One(u32) }",
235 r#"struct One(pub u32);
236
237enum A { One(One) }"#,
238 );
239 }
240
241 #[test]
242 fn test_extract_struct_pub_visibility() {
243 check_assist(
244 extract_struct_from_enum_variant,
245 "pub enum A { <|>One(u32, u32) }",
246 r#"pub struct One(pub u32, pub u32);
247
248pub enum A { One(One) }"#,
249 );
250 }
251
252 #[test]
253 fn test_extract_struct_with_complex_imports() {
254 check_assist(
255 extract_struct_from_enum_variant,
256 r#"mod my_mod {
257 fn another_fn() {
258 let m = my_other_mod::MyEnum::MyField(1, 1);
259 }
260
261 pub mod my_other_mod {
262 fn another_fn() {
263 let m = MyEnum::MyField(1, 1);
264 }
265
266 pub enum MyEnum {
267 <|>MyField(u8, u8),
268 }
269 }
270}
271
272fn another_fn() {
273 let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
274}"#,
275 r#"use my_mod::my_other_mod::MyField;
276
277mod my_mod {
278 use my_other_mod::MyField;
279
280 fn another_fn() {
281 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
282 }
283
284 pub mod my_other_mod {
285 fn another_fn() {
286 let m = MyEnum::MyField(MyField(1, 1));
287 }
288
289 pub struct MyField(pub u8, pub u8);
290
291 pub enum MyEnum {
292 MyField(MyField),
293 }
294 }
295}
296
297fn another_fn() {
298 let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
299}"#,
300 );
301 }
302
303 fn check_not_applicable(ra_fixture: &str) {
304 let fixture =
305 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
306 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
307 }
308
309 #[test]
310 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
311 check_not_applicable("enum A { <|>One }");
312 }
313
314 #[test]
315 fn test_extract_enum_not_applicable_if_struct_exists() {
316 check_not_applicable(
317 r#"struct One;
318 enum A { <|>One(u8) }"#,
319 );
320 }
321}
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs
index 31d6539f7..481baf1a4 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/extract_variable.rs
@@ -9,9 +9,9 @@ use ra_syntax::{
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::mark; 10use test_utils::mark;
11 11
12use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: introduce_variable 14// Assist: extract_variable
15// 15//
16// Extracts subexpression into a variable. 16// Extracts subexpression into a variable.
17// 17//
@@ -27,13 +27,13 @@ use crate::{AssistContext, AssistId, Assists};
27// var_name * 4; 27// var_name * 4;
28// } 28// }
29// ``` 29// ```
30pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 30pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 if ctx.frange.range.is_empty() { 31 if ctx.frange.range.is_empty() {
32 return None; 32 return None;
33 } 33 }
34 let node = ctx.covering_element(); 34 let node = ctx.covering_element();
35 if node.kind() == COMMENT { 35 if node.kind() == COMMENT {
36 mark::hit!(introduce_var_in_comment_is_not_applicable); 36 mark::hit!(extract_var_in_comment_is_not_applicable);
37 return None; 37 return None;
38 } 38 }
39 let expr = node.ancestors().find_map(valid_target_expr)?; 39 let expr = node.ancestors().find_map(valid_target_expr)?;
@@ -43,65 +43,85 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
43 return None; 43 return None;
44 } 44 }
45 let target = expr.syntax().text_range(); 45 let target = expr.syntax().text_range();
46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { 46 acc.add(
47 let mut buf = String::new(); 47 AssistId("extract_variable", AssistKind::RefactorExtract),
48 48 "Extract into variable",
49 if wrap_in_block { 49 target,
50 buf.push_str("{ let var_name = "); 50 move |edit| {
51 } else { 51 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
52 buf.push_str("let var_name = "); 52 Some(field) => field.name_ref(),
53 }; 53 None => None,
54 format_to!(buf, "{}", expr.syntax()); 54 };
55 55
56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 56 let mut buf = String::new();
57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 57
58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 58 let var_name = match &field_shorthand {
59 } else { 59 Some(it) => it.to_string(),
60 false 60 None => "var_name".to_string(),
61 }; 61 };
62 if is_full_stmt { 62 let expr_range = match &field_shorthand {
63 mark::hit!(test_introduce_var_expr_stmt); 63 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
64 if full_stmt.unwrap().semicolon_token().is_none() { 64 None => expr.syntax().text_range(),
65 buf.push_str(";"); 65 };
66
67 if wrap_in_block {
68 format_to!(buf, "{{ let {} = ", var_name);
69 } else {
70 format_to!(buf, "let {} = ", var_name);
71 };
72 format_to!(buf, "{}", expr.syntax());
73
74 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
75 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
76 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
77 } else {
78 false
79 };
80 if is_full_stmt {
81 mark::hit!(test_extract_var_expr_stmt);
82 if full_stmt.unwrap().semicolon_token().is_none() {
83 buf.push_str(";");
84 }
85 match ctx.config.snippet_cap {
86 Some(cap) => {
87 let snip = buf
88 .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
89 edit.replace_snippet(cap, expr_range, snip)
90 }
91 None => edit.replace(expr_range, buf),
92 }
93 return;
94 }
95
96 buf.push_str(";");
97
98 // We want to maintain the indent level,
99 // but we do not want to duplicate possible
100 // extra newlines in the indent block
101 let text = indent.text();
102 if text.starts_with('\n') {
103 buf.push_str("\n");
104 buf.push_str(text.trim_start_matches('\n'));
105 } else {
106 buf.push_str(text);
66 } 107 }
67 let offset = expr.syntax().text_range(); 108
109 edit.replace(expr_range, var_name.clone());
110 let offset = anchor_stmt.text_range().start();
68 match ctx.config.snippet_cap { 111 match ctx.config.snippet_cap {
69 Some(cap) => { 112 Some(cap) => {
70 let snip = buf.replace("let var_name", "let $0var_name"); 113 let snip =
71 edit.replace_snippet(cap, offset, snip) 114 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
115 edit.insert_snippet(cap, offset, snip)
72 } 116 }
73 None => edit.replace(offset, buf), 117 None => edit.insert(offset, buf),
74 } 118 }
75 return;
76 }
77 119
78 buf.push_str(";"); 120 if wrap_in_block {
79 121 edit.insert(anchor_stmt.text_range().end(), " }");
80 // We want to maintain the indent level,
81 // but we do not want to duplicate possible
82 // extra newlines in the indent block
83 let text = indent.text();
84 if text.starts_with('\n') {
85 buf.push_str("\n");
86 buf.push_str(text.trim_start_matches('\n'));
87 } else {
88 buf.push_str(text);
89 }
90
91 edit.replace(expr.syntax().text_range(), "var_name".to_string());
92 let offset = anchor_stmt.text_range().start();
93 match ctx.config.snippet_cap {
94 Some(cap) => {
95 let snip = buf.replace("let var_name", "let $0var_name");
96 edit.insert_snippet(cap, offset, snip)
97 } 122 }
98 None => edit.insert(offset, buf), 123 },
99 } 124 )
100
101 if wrap_in_block {
102 edit.insert(anchor_stmt.text_range().end(), " }");
103 }
104 })
105} 125}
106 126
107/// Check whether the node is a valid expression which can be extracted to a variable. 127/// Check whether the node is a valid expression which can be extracted to a variable.
@@ -118,7 +138,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
118 } 138 }
119} 139}
120 140
121/// Returns the syntax node which will follow the freshly introduced var 141/// Returns the syntax node which will follow the freshly extractd var
122/// and a boolean indicating whether we have to wrap it within a { } block 142/// and a boolean indicating whether we have to wrap it within a { } block
123/// to produce correct code. 143/// to produce correct code.
124/// It can be a statement, the last in a block expression or a wanna be block 144/// It can be a statement, the last in a block expression or a wanna be block
@@ -127,7 +147,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
127 expr.syntax().ancestors().find_map(|node| { 147 expr.syntax().ancestors().find_map(|node| {
128 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { 148 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
129 if expr.syntax() == &node { 149 if expr.syntax() == &node {
130 mark::hit!(test_introduce_var_last_expr); 150 mark::hit!(test_extract_var_last_expr);
131 return Some((node, false)); 151 return Some((node, false));
132 } 152 }
133 } 153 }
@@ -155,9 +175,9 @@ mod tests {
155 use super::*; 175 use super::*;
156 176
157 #[test] 177 #[test]
158 fn test_introduce_var_simple() { 178 fn test_extract_var_simple() {
159 check_assist( 179 check_assist(
160 introduce_variable, 180 extract_variable,
161 r#" 181 r#"
162fn foo() { 182fn foo() {
163 foo(<|>1 + 1<|>); 183 foo(<|>1 + 1<|>);
@@ -171,16 +191,16 @@ fn foo() {
171 } 191 }
172 192
173 #[test] 193 #[test]
174 fn introduce_var_in_comment_is_not_applicable() { 194 fn extract_var_in_comment_is_not_applicable() {
175 mark::check!(introduce_var_in_comment_is_not_applicable); 195 mark::check!(extract_var_in_comment_is_not_applicable);
176 check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); 196 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }");
177 } 197 }
178 198
179 #[test] 199 #[test]
180 fn test_introduce_var_expr_stmt() { 200 fn test_extract_var_expr_stmt() {
181 mark::check!(test_introduce_var_expr_stmt); 201 mark::check!(test_extract_var_expr_stmt);
182 check_assist( 202 check_assist(
183 introduce_variable, 203 extract_variable,
184 r#" 204 r#"
185fn foo() { 205fn foo() {
186 <|>1 + 1<|>; 206 <|>1 + 1<|>;
@@ -191,7 +211,7 @@ fn foo() {
191}"#, 211}"#,
192 ); 212 );
193 check_assist( 213 check_assist(
194 introduce_variable, 214 extract_variable,
195 " 215 "
196fn foo() { 216fn foo() {
197 <|>{ let x = 0; x }<|> 217 <|>{ let x = 0; x }<|>
@@ -206,9 +226,9 @@ fn foo() {
206 } 226 }
207 227
208 #[test] 228 #[test]
209 fn test_introduce_var_part_of_expr_stmt() { 229 fn test_extract_var_part_of_expr_stmt() {
210 check_assist( 230 check_assist(
211 introduce_variable, 231 extract_variable,
212 " 232 "
213fn foo() { 233fn foo() {
214 <|>1<|> + 1; 234 <|>1<|> + 1;
@@ -222,38 +242,42 @@ fn foo() {
222 } 242 }
223 243
224 #[test] 244 #[test]
225 fn test_introduce_var_last_expr() { 245 fn test_extract_var_last_expr() {
226 mark::check!(test_introduce_var_last_expr); 246 mark::check!(test_extract_var_last_expr);
227 check_assist( 247 check_assist(
228 introduce_variable, 248 extract_variable,
229 " 249 r#"
230fn foo() { 250fn foo() {
231 bar(<|>1 + 1<|>) 251 bar(<|>1 + 1<|>)
232}", 252}
233 " 253"#,
254 r#"
234fn foo() { 255fn foo() {
235 let $0var_name = 1 + 1; 256 let $0var_name = 1 + 1;
236 bar(var_name) 257 bar(var_name)
237}", 258}
259"#,
238 ); 260 );
239 check_assist( 261 check_assist(
240 introduce_variable, 262 extract_variable,
241 " 263 r#"
242fn foo() { 264fn foo() {
243 <|>bar(1 + 1)<|> 265 <|>bar(1 + 1)<|>
244}", 266}
245 " 267"#,
268 r#"
246fn foo() { 269fn foo() {
247 let $0var_name = bar(1 + 1); 270 let $0var_name = bar(1 + 1);
248 var_name 271 var_name
249}", 272}
273"#,
250 ) 274 )
251 } 275 }
252 276
253 #[test] 277 #[test]
254 fn test_introduce_var_in_match_arm_no_block() { 278 fn test_extract_var_in_match_arm_no_block() {
255 check_assist( 279 check_assist(
256 introduce_variable, 280 extract_variable,
257 " 281 "
258fn main() { 282fn main() {
259 let x = true; 283 let x = true;
@@ -276,9 +300,9 @@ fn main() {
276 } 300 }
277 301
278 #[test] 302 #[test]
279 fn test_introduce_var_in_match_arm_with_block() { 303 fn test_extract_var_in_match_arm_with_block() {
280 check_assist( 304 check_assist(
281 introduce_variable, 305 extract_variable,
282 " 306 "
283fn main() { 307fn main() {
284 let x = true; 308 let x = true;
@@ -308,9 +332,9 @@ fn main() {
308 } 332 }
309 333
310 #[test] 334 #[test]
311 fn test_introduce_var_in_closure_no_block() { 335 fn test_extract_var_in_closure_no_block() {
312 check_assist( 336 check_assist(
313 introduce_variable, 337 extract_variable,
314 " 338 "
315fn main() { 339fn main() {
316 let lambda = |x: u32| <|>x * 2<|>; 340 let lambda = |x: u32| <|>x * 2<|>;
@@ -325,9 +349,9 @@ fn main() {
325 } 349 }
326 350
327 #[test] 351 #[test]
328 fn test_introduce_var_in_closure_with_block() { 352 fn test_extract_var_in_closure_with_block() {
329 check_assist( 353 check_assist(
330 introduce_variable, 354 extract_variable,
331 " 355 "
332fn main() { 356fn main() {
333 let lambda = |x: u32| { <|>x * 2<|> }; 357 let lambda = |x: u32| { <|>x * 2<|> };
@@ -342,9 +366,9 @@ fn main() {
342 } 366 }
343 367
344 #[test] 368 #[test]
345 fn test_introduce_var_path_simple() { 369 fn test_extract_var_path_simple() {
346 check_assist( 370 check_assist(
347 introduce_variable, 371 extract_variable,
348 " 372 "
349fn main() { 373fn main() {
350 let o = <|>Some(true)<|>; 374 let o = <|>Some(true)<|>;
@@ -360,9 +384,9 @@ fn main() {
360 } 384 }
361 385
362 #[test] 386 #[test]
363 fn test_introduce_var_path_method() { 387 fn test_extract_var_path_method() {
364 check_assist( 388 check_assist(
365 introduce_variable, 389 extract_variable,
366 " 390 "
367fn main() { 391fn main() {
368 let v = <|>bar.foo()<|>; 392 let v = <|>bar.foo()<|>;
@@ -378,9 +402,9 @@ fn main() {
378 } 402 }
379 403
380 #[test] 404 #[test]
381 fn test_introduce_var_return() { 405 fn test_extract_var_return() {
382 check_assist( 406 check_assist(
383 introduce_variable, 407 extract_variable,
384 " 408 "
385fn foo() -> u32 { 409fn foo() -> u32 {
386 <|>return 2 + 2<|>; 410 <|>return 2 + 2<|>;
@@ -396,9 +420,9 @@ fn foo() -> u32 {
396 } 420 }
397 421
398 #[test] 422 #[test]
399 fn test_introduce_var_does_not_add_extra_whitespace() { 423 fn test_extract_var_does_not_add_extra_whitespace() {
400 check_assist( 424 check_assist(
401 introduce_variable, 425 extract_variable,
402 " 426 "
403fn foo() -> u32 { 427fn foo() -> u32 {
404 428
@@ -417,7 +441,7 @@ fn foo() -> u32 {
417 ); 441 );
418 442
419 check_assist( 443 check_assist(
420 introduce_variable, 444 extract_variable,
421 " 445 "
422fn foo() -> u32 { 446fn foo() -> u32 {
423 447
@@ -434,7 +458,7 @@ fn foo() -> u32 {
434 ); 458 );
435 459
436 check_assist( 460 check_assist(
437 introduce_variable, 461 extract_variable,
438 " 462 "
439fn foo() -> u32 { 463fn foo() -> u32 {
440 let foo = 1; 464 let foo = 1;
@@ -460,9 +484,9 @@ fn foo() -> u32 {
460 } 484 }
461 485
462 #[test] 486 #[test]
463 fn test_introduce_var_break() { 487 fn test_extract_var_break() {
464 check_assist( 488 check_assist(
465 introduce_variable, 489 extract_variable,
466 " 490 "
467fn main() { 491fn main() {
468 let result = loop { 492 let result = loop {
@@ -482,9 +506,9 @@ fn main() {
482 } 506 }
483 507
484 #[test] 508 #[test]
485 fn test_introduce_var_for_cast() { 509 fn test_extract_var_for_cast() {
486 check_assist( 510 check_assist(
487 introduce_variable, 511 extract_variable,
488 " 512 "
489fn main() { 513fn main() {
490 let v = <|>0f32 as u32<|>; 514 let v = <|>0f32 as u32<|>;
@@ -500,22 +524,48 @@ fn main() {
500 } 524 }
501 525
502 #[test] 526 #[test]
503 fn test_introduce_var_for_return_not_applicable() { 527 fn extract_var_field_shorthand() {
504 check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); 528 check_assist(
529 extract_variable,
530 r#"
531struct S {
532 foo: i32
533}
534
535fn main() {
536 S { foo: <|>1 + 1<|> }
537}
538"#,
539 r#"
540struct S {
541 foo: i32
542}
543
544fn main() {
545 let $0foo = 1 + 1;
546 S { foo }
547}
548"#,
549 )
550 }
551
552 #[test]
553 fn test_extract_var_for_return_not_applicable() {
554 check_assist_not_applicable(extract_variable, "fn foo() { <|>return<|>; } ");
505 } 555 }
506 556
507 #[test] 557 #[test]
508 fn test_introduce_var_for_break_not_applicable() { 558 fn test_extract_var_for_break_not_applicable() {
509 check_assist_not_applicable(introduce_variable, "fn main() { loop { <|>break<|>; }; }"); 559 check_assist_not_applicable(extract_variable, "fn main() { loop { <|>break<|>; }; }");
510 } 560 }
511 561
512 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 562 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
513 #[test] 563 #[test]
514 fn introduce_var_target() { 564 fn extract_var_target() {
515 check_assist_target(introduce_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); 565 check_assist_target(extract_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2");
516 566
517 check_assist_target( 567 check_assist_target(
518 introduce_variable, 568 extract_variable,
519 " 569 "
520fn main() { 570fn main() {
521 let x = true; 571 let x = true;
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index cc303285b..708e1bc6c 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -8,7 +8,7 @@ use test_utils::mark;
8 8
9use crate::{ 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs}, 10 utils::{render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, Assists, 11 AssistContext, AssistId, AssistKind, Assists,
12}; 12};
13 13
14// Assist: fill_match_arms 14// Assist: fill_match_arms
@@ -51,11 +51,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
51 let module = ctx.sema.scope(expr.syntax()).module()?; 51 let module = ctx.sema.scope(expr.syntax()).module()?;
52 52
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
54 let variants = enum_def.variants(ctx.db); 54 let variants = enum_def.variants(ctx.db());
55 55
56 let mut variants = variants 56 let mut variants = variants
57 .into_iter() 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
@@ -84,11 +84,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
84 // where each tuple represents a proposed match arm. 84 // where each tuple represents a proposed match arm.
85 enum_defs 85 enum_defs
86 .into_iter() 86 .into_iter()
87 .map(|enum_def| enum_def.variants(ctx.db)) 87 .map(|enum_def| enum_def.variants(ctx.db()))
88 .multi_cartesian_product() 88 .multi_cartesian_product()
89 .map(|variants| { 89 .map(|variants| {
90 let patterns = 90 let patterns =
91 variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); 91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
92 ast::Pat::from(make::tuple_pat(patterns)) 92 ast::Pat::from(make::tuple_pat(patterns))
93 }) 93 })
94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
@@ -103,24 +103,37 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
103 } 103 }
104 104
105 let target = match_expr.syntax().text_range(); 105 let target = match_expr.syntax().text_range();
106 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { 106 acc.add(
107 let new_arm_list = match_arm_list.remove_placeholder(); 107 AssistId("fill_match_arms", AssistKind::QuickFix),
108 let n_old_arms = new_arm_list.arms().count(); 108 "Fill match arms",
109 let new_arm_list = new_arm_list.append_arms(missing_arms); 109 target,
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms); 110 |builder| {
111 let old_range = match_arm_list.syntax().text_range(); 111 let new_arm_list = match_arm_list.remove_placeholder();
112 match (first_new_arm, ctx.config.snippet_cap) { 112 let n_old_arms = new_arm_list.arms().count();
113 (Some(first_new_arm), Some(cap)) => { 113 let new_arm_list = new_arm_list.append_arms(missing_arms);
114 let snippet = render_snippet( 114 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
115 cap, 115 let old_range = match_arm_list.syntax().text_range();
116 new_arm_list.syntax(), 116 match (first_new_arm, ctx.config.snippet_cap) {
117 Cursor::Before(first_new_arm.syntax()), 117 (Some(first_new_arm), Some(cap)) => {
118 ); 118 let extend_lifetime;
119 builder.replace_snippet(cap, old_range, snippet); 119 let cursor = match first_new_arm
120 } 120 .syntax()
121 _ => builder.replace(old_range, new_arm_list.to_string()), 121 .descendants()
122 } 122 .find_map(ast::PlaceholderPat::cast)
123 }) 123 {
124 Some(it) => {
125 extend_lifetime = it.syntax().clone();
126 Cursor::Replace(&extend_lifetime)
127 }
128 None => Cursor::Before(first_new_arm.syntax()),
129 };
130 let snippet = render_snippet(cap, new_arm_list.syntax(), cursor);
131 builder.replace_snippet(cap, old_range, snippet);
132 }
133 _ => builder.replace(old_range, new_arm_list.to_string()),
134 }
135 },
136 )
124} 137}
125 138
126fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 139fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
@@ -136,8 +149,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
136} 149}
137 150
138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { 151fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
139 let pat_head = pat.syntax().first_child().map(|node| node.text()); 152 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
140 let var_head = var.syntax().first_child().map(|node| node.text()); 153
154 let pat_head = match pat {
155 Pat::BindPat(bind_pat) => {
156 if let Some(p) = bind_pat.pat() {
157 first_node_text(&p)
158 } else {
159 return false;
160 }
161 }
162 pat => first_node_text(pat),
163 };
164
165 let var_head = first_node_text(var);
141 166
142 pat_head == var_head 167 pat_head == var_head
143} 168}
@@ -274,30 +299,22 @@ mod tests {
274 check_assist( 299 check_assist(
275 fill_match_arms, 300 fill_match_arms,
276 r#" 301 r#"
277 enum A { 302enum A { As, Bs, Cs(Option<i32>) }
278 As, 303fn main() {
279 Bs, 304 match A::As<|> {
280 Cs(Option<i32>), 305 A::Cs(_) | A::Bs => {}
281 } 306 }
282 fn main() { 307}
283 match A::As<|> { 308"#,
284 A::Cs(_) | A::Bs => {}
285 }
286 }
287 "#,
288 r#" 309 r#"
289 enum A { 310enum A { As, Bs, Cs(Option<i32>) }
290 As, 311fn main() {
291 Bs, 312 match A::As {
292 Cs(Option<i32>), 313 A::Cs(_) | A::Bs => {}
293 } 314 $0A::As => {}
294 fn main() { 315 }
295 match A::As { 316}
296 A::Cs(_) | A::Bs => {} 317"#,
297 $0A::As => {}
298 }
299 }
300 "#,
301 ); 318 );
302 } 319 }
303 320
@@ -306,47 +323,55 @@ mod tests {
306 check_assist( 323 check_assist(
307 fill_match_arms, 324 fill_match_arms,
308 r#" 325 r#"
309 enum A { 326enum A { As, Bs, Cs, Ds(String), Es(B) }
310 As, 327enum B { Xs, Ys }
311 Bs, 328fn main() {
312 Cs, 329 match A::As<|> {
313 Ds(String), 330 A::Bs if 0 < 1 => {}
314 Es(B), 331 A::Ds(_value) => { let x = 1; }
315 } 332 A::Es(B::Xs) => (),
316 enum B { 333 }
317 Xs, 334}
318 Ys, 335"#,
319 }
320 fn main() {
321 match A::As<|> {
322 A::Bs if 0 < 1 => {}
323 A::Ds(_value) => { let x = 1; }
324 A::Es(B::Xs) => (),
325 }
326 }
327 "#,
328 r#" 336 r#"
329 enum A { 337enum A { As, Bs, Cs, Ds(String), Es(B) }
330 As, 338enum B { Xs, Ys }
331 Bs, 339fn main() {
332 Cs, 340 match A::As {
333 Ds(String), 341 A::Bs if 0 < 1 => {}
334 Es(B), 342 A::Ds(_value) => { let x = 1; }
335 } 343 A::Es(B::Xs) => (),
336 enum B { 344 $0A::As => {}
337 Xs, 345 A::Cs => {}
338 Ys, 346 }
339 } 347}
340 fn main() { 348"#,
341 match A::As { 349 );
342 A::Bs if 0 < 1 => {} 350 }
343 A::Ds(_value) => { let x = 1; } 351
344 A::Es(B::Xs) => (), 352 #[test]
345 $0A::As => {} 353 fn partial_fill_bind_pat() {
346 A::Cs => {} 354 check_assist(
347 } 355 fill_match_arms,
348 } 356 r#"
349 "#, 357enum A { As, Bs, Cs(Option<i32>) }
358fn main() {
359 match A::As<|> {
360 A::As(_) => {}
361 a @ A::Bs(_) => {}
362 }
363}
364"#,
365 r#"
366enum A { As, Bs, Cs(Option<i32>) }
367fn main() {
368 match A::As {
369 A::As(_) => {}
370 a @ A::Bs(_) => {}
371 A::Cs(${0:_}) => {}
372 }
373}
374"#,
350 ); 375 );
351 } 376 }
352 377
@@ -355,39 +380,27 @@ mod tests {
355 check_assist( 380 check_assist(
356 fill_match_arms, 381 fill_match_arms,
357 r#" 382 r#"
358 enum A { 383enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
359 As,
360 Bs,
361 Cs(String),
362 Ds(String, String),
363 Es { x: usize, y: usize }
364 }
365 384
366 fn main() { 385fn main() {
367 let a = A::As; 386 let a = A::As;
368 match a<|> {} 387 match a<|> {}
369 } 388}
370 "#, 389"#,
371 r#" 390 r#"
372 enum A { 391enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
373 As,
374 Bs,
375 Cs(String),
376 Ds(String, String),
377 Es { x: usize, y: usize }
378 }
379 392
380 fn main() { 393fn main() {
381 let a = A::As; 394 let a = A::As;
382 match a { 395 match a {
383 $0A::As => {} 396 $0A::As => {}
384 A::Bs => {} 397 A::Bs => {}
385 A::Cs(_) => {} 398 A::Cs(_) => {}
386 A::Ds(_, _) => {} 399 A::Ds(_, _) => {}
387 A::Es { x, y } => {} 400 A::Es { x, y } => {}
388 } 401 }
389 } 402}
390 "#, 403"#,
391 ); 404 );
392 } 405 }
393 406
@@ -717,9 +730,9 @@ mod tests {
717fn foo(opt: Option<i32>) { 730fn foo(opt: Option<i32>) {
718 match opt<|> { 731 match opt<|> {
719 } 732 }
720}"#; 733}
721 let before = 734"#;
722 &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE); 735 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
723 736
724 check_assist( 737 check_assist(
725 fill_match_arms, 738 fill_match_arms,
@@ -727,7 +740,7 @@ fn foo(opt: Option<i32>) {
727 r#" 740 r#"
728fn foo(opt: Option<i32>) { 741fn foo(opt: Option<i32>) {
729 match opt { 742 match opt {
730 $0Some(_) => {} 743 Some(${0:_}) => {}
731 None => {} 744 None => {}
732 } 745 }
733} 746}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 9ec42f568..e212557c8 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -1,12 +1,8 @@
1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; 1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId; 2use ra_db::FileId;
3use ra_syntax::{ 3use ra_syntax::{ast, AstNode, TextRange, TextSize};
4 ast, AstNode,
5 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
6 SyntaxNode, TextRange, TextSize,
7};
8 4
9use crate::{AssistContext, AssistId, Assists}; 5use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 6
11// FIXME: this really should be a fix for diagnostic, rather than an assist. 7// FIXME: this really should be a fix for diagnostic, rather than an assist.
12 8
@@ -45,14 +41,14 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
45 }; 41 };
46 42
47 let current_module = ctx.sema.scope(&path.syntax()).module()?; 43 let current_module = ctx.sema.scope(&path.syntax()).module()?;
48 let target_module = def.module(ctx.db)?; 44 let target_module = def.module(ctx.db())?;
49 45
50 let vis = target_module.visibility_of(ctx.db, &def)?; 46 let vis = target_module.visibility_of(ctx.db(), &def)?;
51 if vis.is_visible_from(ctx.db, current_module.into()) { 47 if vis.is_visible_from(ctx.db(), current_module.into()) {
52 return None; 48 return None;
53 }; 49 };
54 50
55 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; 51 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?;
56 52
57 let missing_visibility = 53 let missing_visibility =
58 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 54 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
@@ -62,8 +58,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
62 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), 58 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
63 }; 59 };
64 60
65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 61 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
66 builder.set_file(target_file); 62 builder.edit_file(target_file);
67 match ctx.config.snippet_cap { 63 match ctx.config.snippet_cap {
68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 64 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
69 None => builder.insert(offset, format!("{} ", missing_visibility)), 65 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -76,16 +72,16 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
76 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; 72 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
77 73
78 let current_module = ctx.sema.scope(record_field.syntax()).module()?; 74 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
79 let visibility = record_field_def.visibility(ctx.db); 75 let visibility = record_field_def.visibility(ctx.db());
80 if visibility.is_visible_from(ctx.db, current_module.into()) { 76 if visibility.is_visible_from(ctx.db(), current_module.into()) {
81 return None; 77 return None;
82 } 78 }
83 79
84 let parent = record_field_def.parent_def(ctx.db); 80 let parent = record_field_def.parent_def(ctx.db());
85 let parent_name = parent.name(ctx.db); 81 let parent_name = parent.name(ctx.db());
86 let target_module = parent.module(ctx.db); 82 let target_module = parent.module(ctx.db());
87 83
88 let in_file_source = record_field_def.source(ctx.db); 84 let in_file_source = record_field_def.source(ctx.db());
89 let (offset, target) = match in_file_source.value { 85 let (offset, target) = match in_file_source.value {
90 hir::FieldSource::Named(it) => { 86 hir::FieldSource::Named(it) => {
91 let s = it.syntax(); 87 let s = it.syntax();
@@ -99,14 +95,14 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
99 95
100 let missing_visibility = 96 let missing_visibility =
101 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 97 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
102 let target_file = in_file_source.file_id.original_file(ctx.db); 98 let target_file = in_file_source.file_id.original_file(ctx.db());
103 99
104 let target_name = record_field_def.name(ctx.db); 100 let target_name = record_field_def.name(ctx.db());
105 let assist_label = 101 let assist_label =
106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
107 103
108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 104 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
109 builder.set_file(target_file); 105 builder.edit_file(target_file);
110 match ctx.config.snippet_cap { 106 match ctx.config.snippet_cap {
111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 107 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
112 None => builder.insert(offset, format!("{} ", missing_visibility)), 108 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -177,17 +173,6 @@ fn target_data_for_def(
177 Some((offset, target, target_file, target_name)) 173 Some((offset, target, target_file, target_name))
178} 174}
179 175
180fn vis_offset(node: &SyntaxNode) -> TextSize {
181 node.children_with_tokens()
182 .skip_while(|it| match it.kind() {
183 WHITESPACE | COMMENT | ATTR => true,
184 _ => false,
185 })
186 .next()
187 .map(|it| it.text_range().start())
188 .unwrap_or_else(|| node.text_range().start())
189}
190
191#[cfg(test)] 176#[cfg(test)]
192mod tests { 177mod tests {
193 use crate::tests::{check_assist, check_assist_not_applicable}; 178 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -255,15 +240,14 @@ mod tests {
255 check_assist( 240 check_assist(
256 fix_visibility, 241 fix_visibility,
257 r" 242 r"
258 //- /main.rs 243//- /main.rs
259 mod foo; 244mod foo;
260 fn main() { foo::Foo<|> } 245fn main() { foo::Foo<|> }
261 246
262 //- /foo.rs 247//- /foo.rs
263 struct Foo; 248struct Foo;
264 ", 249",
265 r"$0pub(crate) struct Foo; 250 r"$0pub(crate) struct Foo;
266
267", 251",
268 ); 252 );
269 } 253 }
@@ -279,14 +263,14 @@ mod tests {
279 ); 263 );
280 check_assist( 264 check_assist(
281 fix_visibility, 265 fix_visibility,
282 r"//- /lib.rs 266 r"
283 mod foo; 267//- /lib.rs
284 fn main() { foo::Foo { <|>bar: () }; } 268mod foo;
285 //- /foo.rs 269fn main() { foo::Foo { <|>bar: () }; }
286 pub struct Foo { bar: () } 270//- /foo.rs
287 ", 271pub struct Foo { bar: () }
272",
288 r"pub struct Foo { $0pub(crate) bar: () } 273 r"pub struct Foo { $0pub(crate) bar: () }
289
290", 274",
291 ); 275 );
292 check_assist_not_applicable( 276 check_assist_not_applicable(
@@ -296,12 +280,13 @@ mod tests {
296 ); 280 );
297 check_assist_not_applicable( 281 check_assist_not_applicable(
298 fix_visibility, 282 fix_visibility,
299 r"//- /lib.rs 283 r"
300 mod foo; 284//- /lib.rs
301 fn main() { foo::Foo { <|>bar: () }; } 285mod foo;
302 //- /foo.rs 286fn main() { foo::Foo { <|>bar: () }; }
303 pub struct Foo { pub bar: () } 287//- /foo.rs
304 ", 288pub struct Foo { pub bar: () }
289",
305 ); 290 );
306 } 291 }
307 292
@@ -316,14 +301,14 @@ mod tests {
316 ); 301 );
317 check_assist( 302 check_assist(
318 fix_visibility, 303 fix_visibility,
319 r"//- /lib.rs 304 r"
320 mod foo; 305//- /lib.rs
321 fn main() { foo::Foo::Bar { <|>bar: () }; } 306mod foo;
322 //- /foo.rs 307fn main() { foo::Foo::Bar { <|>bar: () }; }
323 pub enum Foo { Bar { bar: () } } 308//- /foo.rs
324 ", 309pub enum Foo { Bar { bar: () } }
310",
325 r"pub enum Foo { Bar { $0pub(crate) bar: () } } 311 r"pub enum Foo { Bar { $0pub(crate) bar: () } }
326
327", 312",
328 ); 313 );
329 check_assist_not_applicable( 314 check_assist_not_applicable(
@@ -333,12 +318,13 @@ mod tests {
333 ); 318 );
334 check_assist_not_applicable( 319 check_assist_not_applicable(
335 fix_visibility, 320 fix_visibility,
336 r"//- /lib.rs 321 r"
337 mod foo; 322//- /lib.rs
338 fn main() { foo::Foo { <|>bar: () }; } 323mod foo;
339 //- /foo.rs 324fn main() { foo::Foo { <|>bar: () }; }
340 pub struct Foo { pub bar: () } 325//- /foo.rs
341 ", 326pub struct Foo { pub bar: () }
327",
342 ); 328 );
343 } 329 }
344 330
@@ -355,14 +341,14 @@ mod tests {
355 ); 341 );
356 check_assist( 342 check_assist(
357 fix_visibility, 343 fix_visibility,
358 r"//- /lib.rs 344 r"
359 mod foo; 345//- /lib.rs
360 fn main() { foo::Foo { <|>bar: () }; } 346mod foo;
361 //- /foo.rs 347fn main() { foo::Foo { <|>bar: () }; }
362 pub union Foo { bar: () } 348//- /foo.rs
363 ", 349pub union Foo { bar: () }
350",
364 r"pub union Foo { $0pub(crate) bar: () } 351 r"pub union Foo { $0pub(crate) bar: () }
365
366", 352",
367 ); 353 );
368 check_assist_not_applicable( 354 check_assist_not_applicable(
@@ -372,12 +358,13 @@ mod tests {
372 ); 358 );
373 check_assist_not_applicable( 359 check_assist_not_applicable(
374 fix_visibility, 360 fix_visibility,
375 r"//- /lib.rs 361 r"
376 mod foo; 362//- /lib.rs
377 fn main() { foo::Foo { <|>bar: () }; } 363mod foo;
378 //- /foo.rs 364fn main() { foo::Foo { <|>bar: () }; }
379 pub union Foo { pub bar: () } 365//- /foo.rs
380 ", 366pub union Foo { pub bar: () }
367",
381 ); 368 );
382 } 369 }
383 370
@@ -458,19 +445,18 @@ mod tests {
458 check_assist( 445 check_assist(
459 fix_visibility, 446 fix_visibility,
460 r" 447 r"
461 //- /main.rs 448//- /main.rs
462 mod foo; 449mod foo;
463 fn main() { foo::bar<|>::baz(); } 450fn main() { foo::bar<|>::baz(); }
464 451
465 //- /foo.rs 452//- /foo.rs
466 mod bar { 453mod bar {
467 pub fn baz() {} 454 pub fn baz() {}
468 } 455}
469 ", 456",
470 r"$0pub(crate) mod bar { 457 r"$0pub(crate) mod bar {
471 pub fn baz() {} 458 pub fn baz() {}
472} 459}
473
474", 460",
475 ); 461 );
476 462
@@ -486,17 +472,15 @@ mod tests {
486 check_assist( 472 check_assist(
487 fix_visibility, 473 fix_visibility,
488 r" 474 r"
489 //- /main.rs 475//- /main.rs
490 mod foo; 476mod foo;
491 fn main() { foo::bar<|>::baz(); } 477fn main() { foo::bar<|>::baz(); }
492 478
493 //- /foo.rs 479//- /foo.rs
494 mod bar; 480mod bar;
495 481//- /foo/bar.rs
496 //- /foo/bar.rs 482pub fn baz() {}
497 pub fn baz() {} 483",
498 }
499 ",
500 r"$0pub(crate) mod bar; 484 r"$0pub(crate) mod bar;
501", 485",
502 ); 486 );
@@ -506,14 +490,16 @@ mod tests {
506 fn fix_visibility_of_module_declaration_in_other_file() { 490 fn fix_visibility_of_module_declaration_in_other_file() {
507 check_assist( 491 check_assist(
508 fix_visibility, 492 fix_visibility,
509 r"//- /main.rs 493 r"
510 mod foo; 494//- /main.rs
511 fn main() { foo::bar<|>>::baz(); } 495mod foo;
496fn main() { foo::bar<|>>::baz(); }
512 497
513 //- /foo.rs 498//- /foo.rs
514 mod bar { 499mod bar {
515 pub fn baz() {} 500 pub fn baz() {}
516 }", 501}
502",
517 r"$0pub(crate) mod bar { 503 r"$0pub(crate) mod bar {
518 pub fn baz() {} 504 pub fn baz() {}
519} 505}
@@ -525,10 +511,12 @@ mod tests {
525 fn adds_pub_when_target_is_in_another_crate() { 511 fn adds_pub_when_target_is_in_another_crate() {
526 check_assist( 512 check_assist(
527 fix_visibility, 513 fix_visibility,
528 r"//- /main.rs crate:a deps:foo 514 r"
529 foo::Bar<|> 515//- /main.rs crate:a deps:foo
530 //- /lib.rs crate:foo 516foo::Bar<|>
531 struct Bar;", 517//- /lib.rs crate:foo
518struct Bar;
519",
532 r"$0pub struct Bar; 520 r"$0pub struct Bar;
533", 521",
534 ) 522 )
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 573196576..3cd532650 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{AstNode, BinExpr, BinOp}; 1use ra_syntax::ast::{AstNode, BinExpr, BinOp};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_binexpr 5// Assist: flip_binexpr
6// 6//
@@ -33,13 +33,18 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
33 return None; 33 return None;
34 } 34 }
35 35
36 acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { 36 acc.add(
37 if let FlipAction::FlipAndReplaceOp(new_op) = action { 37 AssistId("flip_binexpr", AssistKind::RefactorRewrite),
38 edit.replace(op_range, new_op); 38 "Flip binary expression",
39 } 39 op_range,
40 edit.replace(lhs.text_range(), rhs.text()); 40 |edit| {
41 edit.replace(rhs.text_range(), lhs.text()); 41 if let FlipAction::FlipAndReplaceOp(new_op) = action {
42 }) 42 edit.replace(op_range, new_op);
43 }
44 edit.replace(lhs.text_range(), rhs.text());
45 edit.replace(rhs.text_range(), lhs.text());
46 },
47 )
43} 48}
44 49
45enum FlipAction { 50enum FlipAction {
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index a57a1c463..55a971dc7 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{algo::non_trivia_sibling, Direction, T}; 1use ra_syntax::{algo::non_trivia_sibling, Direction, T};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_comma 5// Assist: flip_comma
6// 6//
@@ -28,10 +28,15 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 return None; 28 return None;
29 } 29 }
30 30
31 acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { 31 acc.add(
32 edit.replace(prev.text_range(), next.to_string()); 32 AssistId("flip_comma", AssistKind::RefactorRewrite),
33 edit.replace(next.text_range(), prev.to_string()); 33 "Flip comma",
34 }) 34 comma.text_range(),
35 |edit| {
36 edit.replace(prev.text_range(), next.to_string());
37 edit.replace(next.text_range(), prev.to_string());
38 },
39 )
35} 40}
36 41
37#[cfg(test)] 42#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index 0115adc8b..1234f4d29 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 Direction, T, 4 Direction, T,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: flip_trait_bound 9// Assist: flip_trait_bound
10// 10//
@@ -33,10 +33,15 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option
33 ); 33 );
34 34
35 let target = plus.text_range(); 35 let target = plus.text_range();
36 acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { 36 acc.add(
37 edit.replace(before.text_range(), after.to_string()); 37 AssistId("flip_trait_bound", AssistKind::RefactorRewrite),
38 edit.replace(after.text_range(), before.to_string()); 38 "Flip trait bounds",
39 }) 39 target,
40 |edit| {
41 edit.replace(before.text_range(), after.to_string());
42 edit.replace(after.text_range(), before.to_string());
43 },
44 )
40} 45}
41 46
42#[cfg(test)] 47#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/generate_derive.rs
index b123b8498..6ccf39900 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/generate_derive.rs
@@ -4,9 +4,9 @@ use ra_syntax::{
4 TextSize, 4 TextSize,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_derive 9// Assist: generate_derive
10// 10//
11// Adds a new `#[derive()]` clause to a struct or enum. 11// Adds a new `#[derive()]` clause to a struct or enum.
12// 12//
@@ -24,32 +24,37 @@ use crate::{AssistContext, AssistId, Assists};
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 27pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?; 28 let cap = ctx.config.snippet_cap?;
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
31 let target = nominal.syntax().text_range(); 31 let target = nominal.syntax().text_range();
32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { 32 acc.add(
33 let derive_attr = nominal 33 AssistId("generate_derive", AssistKind::Generate),
34 .attrs() 34 "Add `#[derive]`",
35 .filter_map(|x| x.as_simple_call()) 35 target,
36 .filter(|(name, _arg)| name == "derive") 36 |builder| {
37 .map(|(_name, arg)| arg) 37 let derive_attr = nominal
38 .next(); 38 .attrs()
39 match derive_attr { 39 .filter_map(|x| x.as_simple_call())
40 None => { 40 .filter(|(name, _arg)| name == "derive")
41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); 41 .map(|(_name, arg)| arg)
42 } 42 .next();
43 Some(tt) => { 43 match derive_attr {
44 // Just move the cursor. 44 None => {
45 builder.insert_snippet( 45 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
46 cap, 46 }
47 tt.syntax().text_range().end() - TextSize::of(')'), 47 Some(tt) => {
48 "$0", 48 // Just move the cursor.
49 ) 49 builder.insert_snippet(
50 } 50 cap,
51 }; 51 tt.syntax().text_range().end() - TextSize::of(')'),
52 }) 52 "$0",
53 )
54 }
55 };
56 },
57 )
53} 58}
54 59
55// Insert `derive` after doc comments. 60// Insert `derive` after doc comments.
@@ -70,12 +75,12 @@ mod tests {
70 #[test] 75 #[test]
71 fn add_derive_new() { 76 fn add_derive_new() {
72 check_assist( 77 check_assist(
73 add_derive, 78 generate_derive,
74 "struct Foo { a: i32, <|>}", 79 "struct Foo { a: i32, <|>}",
75 "#[derive($0)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
76 ); 81 );
77 check_assist( 82 check_assist(
78 add_derive, 83 generate_derive,
79 "struct Foo { <|> a: i32, }", 84 "struct Foo { <|> a: i32, }",
80 "#[derive($0)]\nstruct Foo { a: i32, }", 85 "#[derive($0)]\nstruct Foo { a: i32, }",
81 ); 86 );
@@ -84,7 +89,7 @@ mod tests {
84 #[test] 89 #[test]
85 fn add_derive_existing() { 90 fn add_derive_existing() {
86 check_assist( 91 check_assist(
87 add_derive, 92 generate_derive,
88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 93 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }", 94 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
90 ); 95 );
@@ -93,7 +98,7 @@ mod tests {
93 #[test] 98 #[test]
94 fn add_derive_new_with_doc_comment() { 99 fn add_derive_new_with_doc_comment() {
95 check_assist( 100 check_assist(
96 add_derive, 101 generate_derive,
97 " 102 "
98/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
99/// It does stuff. 104/// It does stuff.
@@ -111,7 +116,7 @@ struct Foo { a: i32, }
111 #[test] 116 #[test]
112 fn add_derive_target() { 117 fn add_derive_target() {
113 check_assist_target( 118 check_assist_target(
114 add_derive, 119 generate_derive,
115 " 120 "
116struct SomeThingIrrelevant; 121struct SomeThingIrrelevant;
117/// `Foo` is a pretty important struct. 122/// `Foo` is a pretty important struct.
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
index 776bddf91..a347e3c2e 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -2,9 +2,9 @@ use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner}; 2use ra_syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark; 3use test_utils::mark;
4 4
5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists};
6 6
7// Assist: add_from_impl_for_enum 7// Assist: generate_from_impl_for_enum
8// 8//
9// Adds a From impl for an enum variant with one tuple field. 9// Adds a From impl for an enum variant with one tuple field.
10// 10//
@@ -21,7 +21,7 @@ use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
21// } 21// }
22// } 22// }
23// ``` 23// ```
24pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; 25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
26 let variant_name = variant.name()?; 26 let variant_name = variant.name()?;
27 let enum_name = variant.parent_enum().name()?; 27 let enum_name = variant.parent_enum().name()?;
@@ -45,8 +45,8 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
45 45
46 let target = variant.syntax().text_range(); 46 let target = variant.syntax().text_range();
47 acc.add( 47 acc.add(
48 AssistId("add_from_impl_for_enum"), 48 AssistId("generate_from_impl_for_enum", AssistKind::Generate),
49 "Add From impl for this enum variant", 49 "Generate `From` impl for this enum variant",
50 target, 50 target,
51 |edit| { 51 |edit| {
52 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
@@ -97,9 +97,9 @@ mod tests {
97 use super::*; 97 use super::*;
98 98
99 #[test] 99 #[test]
100 fn test_add_from_impl_for_enum() { 100 fn test_generate_from_impl_for_enum() {
101 check_assist( 101 check_assist(
102 add_from_impl_for_enum, 102 generate_from_impl_for_enum,
103 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
104 r#"enum A { One(u32) } 104 r#"enum A { One(u32) }
105 105
@@ -112,9 +112,9 @@ impl From<u32> for A {
112 } 112 }
113 113
114 #[test] 114 #[test]
115 fn test_add_from_impl_for_enum_complicated_path() { 115 fn test_generate_from_impl_for_enum_complicated_path() {
116 check_assist( 116 check_assist(
117 add_from_impl_for_enum, 117 generate_from_impl_for_enum,
118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
119 r#"enum A { One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
120 120
@@ -128,8 +128,8 @@ impl From<foo::bar::baz::Boo> for A {
128 128
129 fn check_not_applicable(ra_fixture: &str) { 129 fn check_not_applicable(ra_fixture: &str) {
130 let fixture = 130 let fixture =
131 format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 131 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
132 check_assist_not_applicable(add_from_impl_for_enum, &fixture) 132 check_assist_not_applicable(generate_from_impl_for_enum, &fixture)
133 } 133 }
134 134
135 #[test] 135 #[test]
@@ -166,7 +166,7 @@ impl From<u32> for A {
166 #[test] 166 #[test]
167 fn test_add_from_impl_different_variant_impl_exists() { 167 fn test_add_from_impl_different_variant_impl_exists() {
168 check_assist( 168 check_assist(
169 add_from_impl_for_enum, 169 generate_from_impl_for_enum,
170 r#"enum A { <|>One(u32), Two(String), } 170 r#"enum A { <|>One(u32), Two(String), }
171 171
172impl From<String> for A { 172impl From<String> for A {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/generate_function.rs
index 24f931a85..b721b96bb 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/generate_function.rs
@@ -13,10 +13,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
13use crate::{ 13use crate::{
14 assist_config::SnippetCap, 14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor}, 15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists, 16 AssistContext, AssistId, AssistKind, Assists,
17}; 17};
18 18
19// Assist: add_function 19// Assist: generate_function
20// 20//
21// Adds a stub function with a signature matching the function under the cursor. 21// Adds a stub function with a signature matching the function under the cursor.
22// 22//
@@ -41,7 +41,7 @@ use crate::{
41// } 41// }
42// 42//
43// ``` 43// ```
44pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 44pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; 45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
47 let path = path_expr.path()?; 47 let path = path_expr.path()?;
@@ -62,15 +62,20 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add(AssistId("add_function"), "Add function", target, |builder| { 65 acc.add(
66 let function_template = function_builder.render(); 66 AssistId("generate_function", AssistKind::Generate),
67 builder.set_file(function_template.file); 67 format!("Generate `{}` function", function_builder.fn_name),
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 target,
69 match ctx.config.snippet_cap { 69 |builder| {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 let function_template = function_builder.render();
71 None => builder.insert(function_template.insert_offset, new_fn), 71 builder.edit_file(function_template.file);
72 } 72 let new_fn = function_template.to_string(ctx.config.snippet_cap);
73 }) 73 match ctx.config.snippet_cap {
74 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
75 None => builder.insert(function_template.insert_offset, new_fn),
76 }
77 },
78 )
74} 79}
75 80
76struct FunctionTemplate { 81struct FunctionTemplate {
@@ -117,7 +122,7 @@ impl FunctionBuilder {
117 let mut file = ctx.frange.file_id; 122 let mut file = ctx.frange.file_id;
118 let target = match &target_module { 123 let target = match &target_module {
119 Some(target_module) => { 124 Some(target_module) => {
120 let module_source = target_module.definition_source(ctx.db); 125 let module_source = target_module.definition_source(ctx.db());
121 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; 126 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
122 file = in_file; 127 file = in_file;
123 target 128 target
@@ -269,7 +274,7 @@ fn fn_arg_type(
269 return None; 274 return None;
270 } 275 }
271 276
272 if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { 277 if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
273 Some(rendered) 278 Some(rendered)
274 } else { 279 } else {
275 None 280 None
@@ -333,7 +338,7 @@ mod tests {
333 #[test] 338 #[test]
334 fn add_function_with_no_args() { 339 fn add_function_with_no_args() {
335 check_assist( 340 check_assist(
336 add_function, 341 generate_function,
337 r" 342 r"
338fn foo() { 343fn foo() {
339 bar<|>(); 344 bar<|>();
@@ -356,7 +361,7 @@ fn bar() {
356 // This ensures that the function is correctly generated 361 // This ensures that the function is correctly generated
357 // in the next outer mod or file 362 // in the next outer mod or file
358 check_assist( 363 check_assist(
359 add_function, 364 generate_function,
360 r" 365 r"
361impl Foo { 366impl Foo {
362 fn foo() { 367 fn foo() {
@@ -382,7 +387,7 @@ fn bar() {
382 fn add_function_directly_after_current_block() { 387 fn add_function_directly_after_current_block() {
383 // The new fn should not be created at the end of the file or module 388 // The new fn should not be created at the end of the file or module
384 check_assist( 389 check_assist(
385 add_function, 390 generate_function,
386 r" 391 r"
387fn foo1() { 392fn foo1() {
388 bar<|>(); 393 bar<|>();
@@ -407,7 +412,7 @@ fn foo2() {}
407 #[test] 412 #[test]
408 fn add_function_with_no_args_in_same_module() { 413 fn add_function_with_no_args_in_same_module() {
409 check_assist( 414 check_assist(
410 add_function, 415 generate_function,
411 r" 416 r"
412mod baz { 417mod baz {
413 fn foo() { 418 fn foo() {
@@ -432,7 +437,7 @@ mod baz {
432 #[test] 437 #[test]
433 fn add_function_with_function_call_arg() { 438 fn add_function_with_function_call_arg() {
434 check_assist( 439 check_assist(
435 add_function, 440 generate_function,
436 r" 441 r"
437struct Baz; 442struct Baz;
438fn baz() -> Baz { todo!() } 443fn baz() -> Baz { todo!() }
@@ -457,7 +462,7 @@ fn bar(baz: Baz) {
457 #[test] 462 #[test]
458 fn add_function_with_method_call_arg() { 463 fn add_function_with_method_call_arg() {
459 check_assist( 464 check_assist(
460 add_function, 465 generate_function,
461 r" 466 r"
462struct Baz; 467struct Baz;
463impl Baz { 468impl Baz {
@@ -490,7 +495,7 @@ fn bar(baz: Baz) {
490 #[test] 495 #[test]
491 fn add_function_with_string_literal_arg() { 496 fn add_function_with_string_literal_arg() {
492 check_assist( 497 check_assist(
493 add_function, 498 generate_function,
494 r#" 499 r#"
495fn foo() { 500fn foo() {
496 <|>bar("bar") 501 <|>bar("bar")
@@ -511,7 +516,7 @@ fn bar(arg: &str) {
511 #[test] 516 #[test]
512 fn add_function_with_char_literal_arg() { 517 fn add_function_with_char_literal_arg() {
513 check_assist( 518 check_assist(
514 add_function, 519 generate_function,
515 r#" 520 r#"
516fn foo() { 521fn foo() {
517 <|>bar('x') 522 <|>bar('x')
@@ -532,7 +537,7 @@ fn bar(arg: char) {
532 #[test] 537 #[test]
533 fn add_function_with_int_literal_arg() { 538 fn add_function_with_int_literal_arg() {
534 check_assist( 539 check_assist(
535 add_function, 540 generate_function,
536 r" 541 r"
537fn foo() { 542fn foo() {
538 <|>bar(42) 543 <|>bar(42)
@@ -553,7 +558,7 @@ fn bar(arg: i32) {
553 #[test] 558 #[test]
554 fn add_function_with_cast_int_literal_arg() { 559 fn add_function_with_cast_int_literal_arg() {
555 check_assist( 560 check_assist(
556 add_function, 561 generate_function,
557 r" 562 r"
558fn foo() { 563fn foo() {
559 <|>bar(42 as u8) 564 <|>bar(42 as u8)
@@ -576,7 +581,7 @@ fn bar(arg: u8) {
576 // Ensures that the name of the cast type isn't used 581 // Ensures that the name of the cast type isn't used
577 // in the generated function signature. 582 // in the generated function signature.
578 check_assist( 583 check_assist(
579 add_function, 584 generate_function,
580 r" 585 r"
581fn foo() { 586fn foo() {
582 let x = 42; 587 let x = 42;
@@ -599,7 +604,7 @@ fn bar(x: u8) {
599 #[test] 604 #[test]
600 fn add_function_with_variable_arg() { 605 fn add_function_with_variable_arg() {
601 check_assist( 606 check_assist(
602 add_function, 607 generate_function,
603 r" 608 r"
604fn foo() { 609fn foo() {
605 let worble = (); 610 let worble = ();
@@ -622,7 +627,7 @@ fn bar(worble: ()) {
622 #[test] 627 #[test]
623 fn add_function_with_impl_trait_arg() { 628 fn add_function_with_impl_trait_arg() {
624 check_assist( 629 check_assist(
625 add_function, 630 generate_function,
626 r" 631 r"
627trait Foo {} 632trait Foo {}
628fn foo() -> impl Foo { 633fn foo() -> impl Foo {
@@ -651,7 +656,7 @@ fn bar(foo: impl Foo) {
651 #[test] 656 #[test]
652 fn borrowed_arg() { 657 fn borrowed_arg() {
653 check_assist( 658 check_assist(
654 add_function, 659 generate_function,
655 r" 660 r"
656struct Baz; 661struct Baz;
657fn baz() -> Baz { todo!() } 662fn baz() -> Baz { todo!() }
@@ -678,7 +683,7 @@ fn bar(baz: &Baz) {
678 #[test] 683 #[test]
679 fn add_function_with_qualified_path_arg() { 684 fn add_function_with_qualified_path_arg() {
680 check_assist( 685 check_assist(
681 add_function, 686 generate_function,
682 r" 687 r"
683mod Baz { 688mod Baz {
684 pub struct Bof; 689 pub struct Bof;
@@ -709,7 +714,7 @@ fn bar(baz: Baz::Bof) {
709 // FIXME fix printing the generics of a `Ty` to make this test pass 714 // FIXME fix printing the generics of a `Ty` to make this test pass
710 fn add_function_with_generic_arg() { 715 fn add_function_with_generic_arg() {
711 check_assist( 716 check_assist(
712 add_function, 717 generate_function,
713 r" 718 r"
714fn foo<T>(t: T) { 719fn foo<T>(t: T) {
715 <|>bar(t) 720 <|>bar(t)
@@ -732,7 +737,7 @@ fn bar<T>(t: T) {
732 // FIXME Fix function type printing to make this test pass 737 // FIXME Fix function type printing to make this test pass
733 fn add_function_with_fn_arg() { 738 fn add_function_with_fn_arg() {
734 check_assist( 739 check_assist(
735 add_function, 740 generate_function,
736 r" 741 r"
737struct Baz; 742struct Baz;
738impl Baz { 743impl Baz {
@@ -763,7 +768,7 @@ fn bar(arg: fn() -> Baz) {
763 // FIXME Fix closure type printing to make this test pass 768 // FIXME Fix closure type printing to make this test pass
764 fn add_function_with_closure_arg() { 769 fn add_function_with_closure_arg() {
765 check_assist( 770 check_assist(
766 add_function, 771 generate_function,
767 r" 772 r"
768fn foo() { 773fn foo() {
769 let closure = |x: i64| x - 1; 774 let closure = |x: i64| x - 1;
@@ -786,7 +791,7 @@ fn bar(closure: impl Fn(i64) -> i64) {
786 #[test] 791 #[test]
787 fn unresolveable_types_default_to_unit() { 792 fn unresolveable_types_default_to_unit() {
788 check_assist( 793 check_assist(
789 add_function, 794 generate_function,
790 r" 795 r"
791fn foo() { 796fn foo() {
792 <|>bar(baz) 797 <|>bar(baz)
@@ -807,7 +812,7 @@ fn bar(baz: ()) {
807 #[test] 812 #[test]
808 fn arg_names_dont_overlap() { 813 fn arg_names_dont_overlap() {
809 check_assist( 814 check_assist(
810 add_function, 815 generate_function,
811 r" 816 r"
812struct Baz; 817struct Baz;
813fn baz() -> Baz { Baz } 818fn baz() -> Baz { Baz }
@@ -832,7 +837,7 @@ fn bar(baz_1: Baz, baz_2: Baz) {
832 #[test] 837 #[test]
833 fn arg_name_counters_start_at_1_per_name() { 838 fn arg_name_counters_start_at_1_per_name() {
834 check_assist( 839 check_assist(
835 add_function, 840 generate_function,
836 r#" 841 r#"
837struct Baz; 842struct Baz;
838fn baz() -> Baz { Baz } 843fn baz() -> Baz { Baz }
@@ -857,7 +862,7 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
857 #[test] 862 #[test]
858 fn add_function_in_module() { 863 fn add_function_in_module() {
859 check_assist( 864 check_assist(
860 add_function, 865 generate_function,
861 r" 866 r"
862mod bar {} 867mod bar {}
863 868
@@ -885,7 +890,7 @@ fn foo() {
885 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 890 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
886 fn qualified_path_uses_correct_scope() { 891 fn qualified_path_uses_correct_scope() {
887 check_assist( 892 check_assist(
888 add_function, 893 generate_function,
889 " 894 "
890mod foo { 895mod foo {
891 pub struct Foo; 896 pub struct Foo;
@@ -916,7 +921,7 @@ fn baz(foo: foo::Foo) {
916 #[test] 921 #[test]
917 fn add_function_in_module_containing_other_items() { 922 fn add_function_in_module_containing_other_items() {
918 check_assist( 923 check_assist(
919 add_function, 924 generate_function,
920 r" 925 r"
921mod bar { 926mod bar {
922 fn something_else() {} 927 fn something_else() {}
@@ -945,7 +950,7 @@ fn foo() {
945 #[test] 950 #[test]
946 fn add_function_in_nested_module() { 951 fn add_function_in_nested_module() {
947 check_assist( 952 check_assist(
948 add_function, 953 generate_function,
949 r" 954 r"
950mod bar { 955mod bar {
951 mod baz {} 956 mod baz {}
@@ -974,7 +979,7 @@ fn foo() {
974 #[test] 979 #[test]
975 fn add_function_in_another_file() { 980 fn add_function_in_another_file() {
976 check_assist( 981 check_assist(
977 add_function, 982 generate_function,
978 r" 983 r"
979//- /main.rs 984//- /main.rs
980mod foo; 985mod foo;
@@ -996,7 +1001,7 @@ pub(crate) fn bar() {
996 #[test] 1001 #[test]
997 fn add_function_not_applicable_if_function_already_exists() { 1002 fn add_function_not_applicable_if_function_already_exists() {
998 check_assist_not_applicable( 1003 check_assist_not_applicable(
999 add_function, 1004 generate_function,
1000 r" 1005 r"
1001fn foo() { 1006fn foo() {
1002 bar<|>(); 1007 bar<|>();
@@ -1013,7 +1018,7 @@ fn bar() {}
1013 // bar is resolved, but baz isn't. 1018 // bar is resolved, but baz isn't.
1014 // The assist is only active if the cursor is on an unresolved path, 1019 // The assist is only active if the cursor is on an unresolved path,
1015 // but the assist should only be offered if the path is a function call. 1020 // but the assist should only be offered if the path is a function call.
1016 add_function, 1021 generate_function,
1017 r" 1022 r"
1018fn foo() { 1023fn foo() {
1019 bar(b<|>az); 1024 bar(b<|>az);
@@ -1028,7 +1033,7 @@ fn bar(baz: ()) {}
1028 #[ignore] 1033 #[ignore]
1029 fn create_method_with_no_args() { 1034 fn create_method_with_no_args() {
1030 check_assist( 1035 check_assist(
1031 add_function, 1036 generate_function,
1032 r" 1037 r"
1033struct Foo; 1038struct Foo;
1034impl Foo { 1039impl Foo {
diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs
new file mode 100644
index 000000000..cbbac1d7f
--- /dev/null
+++ b/crates/ra_assists/src/handlers/generate_impl.rs
@@ -0,0 +1,109 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, AssistKind, Assists};
5
6// Assist: generate_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(
30 AssistId("generate_impl", AssistKind::Generate),
31 format!("Generate impl for `{}`", name),
32 target,
33 |edit| {
34 let type_params = nominal.type_param_list();
35 let start_offset = nominal.syntax().text_range().end();
36 let mut buf = String::new();
37 buf.push_str("\n\nimpl");
38 if let Some(type_params) = &type_params {
39 format_to!(buf, "{}", type_params.syntax());
40 }
41 buf.push_str(" ");
42 buf.push_str(name.text().as_str());
43 if let Some(type_params) = type_params {
44 let lifetime_params = type_params
45 .lifetime_params()
46 .filter_map(|it| it.lifetime_token())
47 .map(|it| it.text().clone());
48 let type_params = type_params
49 .type_params()
50 .filter_map(|it| it.name())
51 .map(|it| it.text().clone());
52
53 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
54 format_to!(buf, "<{}>", generic_params)
55 }
56 match ctx.config.snippet_cap {
57 Some(cap) => {
58 buf.push_str(" {\n $0\n}");
59 edit.insert_snippet(cap, start_offset, buf);
60 }
61 None => {
62 buf.push_str(" {\n}");
63 edit.insert(start_offset, buf);
64 }
65 }
66 },
67 )
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::tests::{check_assist, check_assist_target};
73
74 use super::*;
75
76 #[test]
77 fn test_add_impl() {
78 check_assist(
79 generate_impl,
80 "struct Foo {<|>}\n",
81 "struct Foo {}\n\nimpl Foo {\n $0\n}\n",
82 );
83 check_assist(
84 generate_impl,
85 "struct Foo<T: Clone> {<|>}",
86 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
87 );
88 check_assist(
89 generate_impl,
90 "struct Foo<'a, T: Foo<'a>> {<|>}",
91 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
92 );
93 }
94
95 #[test]
96 fn add_impl_target() {
97 check_assist_target(
98 generate_impl,
99 "
100struct SomeThingIrrelevant;
101/// Has a lifetime parameter
102struct Foo<'a, T: Foo<'a>> {<|>}
103struct EvenMoreIrrelevant;
104",
105 "/// Has a lifetime parameter
106struct Foo<'a, T: Foo<'a>> {}",
107 );
108 }
109}
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/generate_new.rs
index 837aa8377..e27def1d8 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/generate_new.rs
@@ -7,9 +7,9 @@ use ra_syntax::{
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
10use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
11 11
12// Assist: add_new 12// Assist: generate_new
13// 13//
14// Adds a new inherent impl for a type. 14// Adds a new inherent impl for a type.
15// 15//
@@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists};
29// } 29// }
30// 30//
31// ``` 31// ```
32pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; 33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
34 34
35 // We want to only apply this to non-union structs with named fields 35 // We want to only apply this to non-union structs with named fields
@@ -42,7 +42,7 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43
44 let target = strukt.syntax().text_range(); 44 let target = strukt.syntax().text_range();
45 acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { 45 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
46 let mut buf = String::with_capacity(512); 46 let mut buf = String::with_capacity(512);
47 47
48 if impl_def.is_some() { 48 if impl_def.is_some() {
@@ -122,7 +122,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
122// FIXME: change the new fn checking to a more semantic approach when that's more 122// FIXME: change the new fn checking to a more semantic approach when that's more
123// viable (e.g. we process proc macros, etc) 123// viable (e.g. we process proc macros, etc)
124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { 124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
125 let db = ctx.db; 125 let db = ctx.db();
126 let module = strukt.syntax().ancestors().find(|node| { 126 let module = strukt.syntax().ancestors().find(|node| {
127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) 127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
128 })?; 128 })?;
@@ -181,10 +181,10 @@ mod tests {
181 181
182 #[test] 182 #[test]
183 #[rustfmt::skip] 183 #[rustfmt::skip]
184 fn test_add_new() { 184 fn test_generate_new() {
185 // Check output of generation 185 // Check output of generation
186 check_assist( 186 check_assist(
187 add_new, 187 generate_new,
188"struct Foo {<|>}", 188"struct Foo {<|>}",
189"struct Foo {} 189"struct Foo {}
190 190
@@ -194,7 +194,7 @@ impl Foo {
194", 194",
195 ); 195 );
196 check_assist( 196 check_assist(
197 add_new, 197 generate_new,
198"struct Foo<T: Clone> {<|>}", 198"struct Foo<T: Clone> {<|>}",
199"struct Foo<T: Clone> {} 199"struct Foo<T: Clone> {}
200 200
@@ -204,7 +204,7 @@ impl<T: Clone> Foo<T> {
204", 204",
205 ); 205 );
206 check_assist( 206 check_assist(
207 add_new, 207 generate_new,
208"struct Foo<'a, T: Foo<'a>> {<|>}", 208"struct Foo<'a, T: Foo<'a>> {<|>}",
209"struct Foo<'a, T: Foo<'a>> {} 209"struct Foo<'a, T: Foo<'a>> {}
210 210
@@ -214,7 +214,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
214", 214",
215 ); 215 );
216 check_assist( 216 check_assist(
217 add_new, 217 generate_new,
218"struct Foo { baz: String <|>}", 218"struct Foo { baz: String <|>}",
219"struct Foo { baz: String } 219"struct Foo { baz: String }
220 220
@@ -224,7 +224,7 @@ impl Foo {
224", 224",
225 ); 225 );
226 check_assist( 226 check_assist(
227 add_new, 227 generate_new,
228"struct Foo { baz: String, qux: Vec<i32> <|>}", 228"struct Foo { baz: String, qux: Vec<i32> <|>}",
229"struct Foo { baz: String, qux: Vec<i32> } 229"struct Foo { baz: String, qux: Vec<i32> }
230 230
@@ -236,7 +236,7 @@ impl Foo {
236 236
237 // Check that visibility modifiers don't get brought in for fields 237 // Check that visibility modifiers don't get brought in for fields
238 check_assist( 238 check_assist(
239 add_new, 239 generate_new,
240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", 240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}",
241"struct Foo { pub baz: String, pub qux: Vec<i32> } 241"struct Foo { pub baz: String, pub qux: Vec<i32> }
242 242
@@ -248,7 +248,7 @@ impl Foo {
248 248
249 // Check that it reuses existing impls 249 // Check that it reuses existing impls
250 check_assist( 250 check_assist(
251 add_new, 251 generate_new,
252"struct Foo {<|>} 252"struct Foo {<|>}
253 253
254impl Foo {} 254impl Foo {}
@@ -261,7 +261,7 @@ impl Foo {
261", 261",
262 ); 262 );
263 check_assist( 263 check_assist(
264 add_new, 264 generate_new,
265"struct Foo {<|>} 265"struct Foo {<|>}
266 266
267impl Foo { 267impl Foo {
@@ -279,7 +279,7 @@ impl Foo {
279 ); 279 );
280 280
281 check_assist( 281 check_assist(
282 add_new, 282 generate_new,
283"struct Foo {<|>} 283"struct Foo {<|>}
284 284
285impl Foo { 285impl Foo {
@@ -304,7 +304,7 @@ impl Foo {
304 304
305 // Check visibility of new fn based on struct 305 // Check visibility of new fn based on struct
306 check_assist( 306 check_assist(
307 add_new, 307 generate_new,
308"pub struct Foo {<|>}", 308"pub struct Foo {<|>}",
309"pub struct Foo {} 309"pub struct Foo {}
310 310
@@ -314,7 +314,7 @@ impl Foo {
314", 314",
315 ); 315 );
316 check_assist( 316 check_assist(
317 add_new, 317 generate_new,
318"pub(crate) struct Foo {<|>}", 318"pub(crate) struct Foo {<|>}",
319"pub(crate) struct Foo {} 319"pub(crate) struct Foo {}
320 320
@@ -326,9 +326,9 @@ impl Foo {
326 } 326 }
327 327
328 #[test] 328 #[test]
329 fn add_new_not_applicable_if_fn_exists() { 329 fn generate_new_not_applicable_if_fn_exists() {
330 check_assist_not_applicable( 330 check_assist_not_applicable(
331 add_new, 331 generate_new,
332 " 332 "
333struct Foo {<|>} 333struct Foo {<|>}
334 334
@@ -340,7 +340,7 @@ impl Foo {
340 ); 340 );
341 341
342 check_assist_not_applicable( 342 check_assist_not_applicable(
343 add_new, 343 generate_new,
344 " 344 "
345struct Foo {<|>} 345struct Foo {<|>}
346 346
@@ -353,9 +353,9 @@ impl Foo {
353 } 353 }
354 354
355 #[test] 355 #[test]
356 fn add_new_target() { 356 fn generate_new_target() {
357 check_assist_target( 357 check_assist_target(
358 add_new, 358 generate_new,
359 " 359 "
360struct SomeThingIrrelevant; 360struct SomeThingIrrelevant;
361/// Has a lifetime parameter 361/// Has a lifetime parameter
@@ -370,7 +370,7 @@ struct Foo<'a, T: Foo<'a>> {}",
370 #[test] 370 #[test]
371 fn test_unrelated_new() { 371 fn test_unrelated_new() {
372 check_assist( 372 check_assist(
373 add_new, 373 generate_new,
374 r##" 374 r##"
375pub struct AstId<N: AstNode> { 375pub struct AstId<N: AstNode> {
376 file_id: HirFileId, 376 file_id: HirFileId,
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index d26e68847..2fdfabaf5 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -7,7 +7,7 @@ use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
10 AssistId, 10 AssistId, AssistKind,
11};</