aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_config.rs27
-rw-r--r--crates/ra_assists/src/assist_context.rs50
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs51
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs28
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs23
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs21
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs145
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs34
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs98
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs56
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs134
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs8
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs29
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs98
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs548
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs18
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs232
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs559
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs14
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs2
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs14
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs63
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs128
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs6
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs22
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs25
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs8
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs33
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs26
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs39
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs5
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs6
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs29
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs5
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs40
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs43
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs6
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs34
-rw-r--r--crates/ra_assists/src/lib.rs20
-rw-r--r--crates/ra_assists/src/marks.rs12
-rw-r--r--crates/ra_assists/src/tests.rs30
-rw-r--r--crates/ra_assists/src/tests/generated.rs68
-rw-r--r--crates/ra_assists/src/utils.rs78
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs6
44 files changed, 1622 insertions, 1299 deletions
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs
new file mode 100644
index 000000000..c0a0226fb
--- /dev/null
+++ b/crates/ra_assists/src/assist_config.rs
@@ -0,0 +1,27 @@
1//! Settings for tweaking assists.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AssistConfig {
9 pub snippet_cap: Option<SnippetCap>,
10}
11
12impl AssistConfig {
13 pub fn allow_snippets(&mut self, yes: bool) {
14 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
15 }
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct SnippetCap {
20 _private: (),
21}
22
23impl Default for AssistConfig {
24 fn default() -> Self {
25 AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) }
26 }
27}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index a680f752b..f3af70a3e 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -15,7 +15,10 @@ use ra_syntax::{
15}; 15};
16use ra_text_edit::TextEditBuilder; 16use ra_text_edit::TextEditBuilder;
17 17
18use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; 18use crate::{
19 assist_config::{AssistConfig, SnippetCap},
20 Assist, AssistId, GroupLabel, ResolvedAssist,
21};
19 22
20/// `AssistContext` allows to apply an assist or check if it could be applied. 23/// `AssistContext` allows to apply an assist or check if it could be applied.
21/// 24///
@@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
48/// moment, because the LSP API is pretty awkward in this place, and it's much 51/// moment, because the LSP API is pretty awkward in this place, and it's much
49/// easier to just compute the edit eagerly :-) 52/// easier to just compute the edit eagerly :-)
50pub(crate) struct AssistContext<'a> { 53pub(crate) struct AssistContext<'a> {
54 pub(crate) config: &'a AssistConfig,
51 pub(crate) sema: Semantics<'a, RootDatabase>, 55 pub(crate) sema: Semantics<'a, RootDatabase>,
52 pub(crate) db: &'a RootDatabase, 56 pub(crate) db: &'a RootDatabase,
53 pub(crate) frange: FileRange, 57 pub(crate) frange: FileRange,
@@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> {
55} 59}
56 60
57impl<'a> AssistContext<'a> { 61impl<'a> AssistContext<'a> {
58 pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { 62 pub(crate) fn new(
63 sema: Semantics<'a, RootDatabase>,
64 config: &'a AssistConfig,
65 frange: FileRange,
66 ) -> AssistContext<'a> {
59 let source_file = sema.parse(frange.file_id); 67 let source_file = sema.parse(frange.file_id);
60 let db = sema.db; 68 let db = sema.db;
61 AssistContext { sema, db, frange, source_file } 69 AssistContext { config, sema, db, frange, source_file }
62 } 70 }
63 71
64 // NB, this ignores active selection. 72 // NB, this ignores active selection.
@@ -163,13 +171,13 @@ impl Assists {
163 171
164pub(crate) struct AssistBuilder { 172pub(crate) struct AssistBuilder {
165 edit: TextEditBuilder, 173 edit: TextEditBuilder,
166 cursor_position: Option<TextSize>,
167 file: FileId, 174 file: FileId,
175 is_snippet: bool,
168} 176}
169 177
170impl AssistBuilder { 178impl AssistBuilder {
171 pub(crate) fn new(file: FileId) -> AssistBuilder { 179 pub(crate) fn new(file: FileId) -> AssistBuilder {
172 AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } 180 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
173 } 181 }
174 182
175 /// Remove specified `range` of text. 183 /// Remove specified `range` of text.
@@ -180,10 +188,30 @@ impl AssistBuilder {
180 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { 188 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
181 self.edit.insert(offset, text.into()) 189 self.edit.insert(offset, text.into())
182 } 190 }
191 /// Append specified `snippet` at the given `offset`
192 pub(crate) fn insert_snippet(
193 &mut self,
194 _cap: SnippetCap,
195 offset: TextSize,
196 snippet: impl Into<String>,
197 ) {
198 self.is_snippet = true;
199 self.insert(offset, snippet);
200 }
183 /// Replaces specified `range` of text with a given string. 201 /// Replaces specified `range` of text with a given string.
184 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { 202 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
185 self.edit.replace(range, replace_with.into()) 203 self.edit.replace(range, replace_with.into())
186 } 204 }
205 /// Replaces specified `range` of text with a given `snippet`.
206 pub(crate) fn replace_snippet(
207 &mut self,
208 _cap: SnippetCap,
209 range: TextRange,
210 snippet: impl Into<String>,
211 ) {
212 self.is_snippet = true;
213 self.replace(range, snippet);
214 }
187 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 215 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
188 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 216 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
189 } 217 }
@@ -207,10 +235,6 @@ impl AssistBuilder {
207 algo::diff(&node, &new).into_text_edit(&mut self.edit) 235 algo::diff(&node, &new).into_text_edit(&mut self.edit)
208 } 236 }
209 237
210 /// Specify desired position of the cursor after the assist is applied.
211 pub(crate) fn set_cursor(&mut self, offset: TextSize) {
212 self.cursor_position = Some(offset)
213 }
214 // FIXME: better API 238 // FIXME: better API
215 pub(crate) fn set_file(&mut self, assist_file: FileId) { 239 pub(crate) fn set_file(&mut self, assist_file: FileId) {
216 self.file = assist_file; 240 self.file = assist_file;
@@ -224,10 +248,10 @@ impl AssistBuilder {
224 248
225 fn finish(self, change_label: String) -> SourceChange { 249 fn finish(self, change_label: String) -> SourceChange {
226 let edit = self.edit.finish(); 250 let edit = self.edit.finish();
227 if edit.is_empty() && self.cursor_position.is_none() { 251 let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file);
228 panic!("Only call `add_assist` if the assist can be applied") 252 if self.is_snippet {
253 res.is_snippet = true;
229 } 254 }
230 SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } 255 res
231 .into_source_change(self.file)
232 } 256 }
233} 257}
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index 2baeb8607..fa70c8496 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -25,7 +25,7 @@ use crate::{
25// struct S; 25// struct S;
26// 26//
27// impl Debug for S { 27// impl Debug for S {
28// 28// $0
29// } 29// }
30// ``` 30// ```
31pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -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, |edit| { 55 acc.add(AssistId("add_custom_impl"), 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()
@@ -63,20 +63,11 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
63 let has_more_derives = !new_attr_input.is_empty(); 63 let has_more_derives = !new_attr_input.is_empty();
64 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); 64 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
65 65
66 let mut buf = String::new(); 66 if has_more_derives {
67 buf.push_str("\n\nimpl "); 67 builder.replace(input.syntax().text_range(), new_attr_input);
68 buf.push_str(trait_token.text().as_str());
69 buf.push_str(" for ");
70 buf.push_str(annotated_name.as_str());
71 buf.push_str(" {\n");
72
73 let cursor_delta = if has_more_derives {
74 let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input);
75 edit.replace(input.syntax().text_range(), new_attr_input);
76 delta
77 } else { 68 } else {
78 let attr_range = attr.syntax().text_range(); 69 let attr_range = attr.syntax().text_range();
79 edit.delete(attr_range); 70 builder.delete(attr_range);
80 71
81 let line_break_range = attr 72 let line_break_range = attr
82 .syntax() 73 .syntax()
@@ -84,14 +75,24 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
84 .filter(|t| t.kind() == WHITESPACE) 75 .filter(|t| t.kind() == WHITESPACE)
85 .map(|t| t.text_range()) 76 .map(|t| t.text_range())
86 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); 77 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
87 edit.delete(line_break_range); 78 builder.delete(line_break_range);
88 79 }
89 attr_range.len() + line_break_range.len() 80
90 }; 81 match ctx.config.snippet_cap {
91 82 Some(cap) => {
92 edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); 83 builder.insert_snippet(
93 buf.push_str("\n}"); 84 cap,
94 edit.insert(start_offset, buf); 85 start_offset,
86 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name),
87 );
88 }
89 None => {
90 builder.insert(
91 start_offset,
92 format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name),
93 );
94 }
95 }
95 }) 96 })
96} 97}
97 98
@@ -117,7 +118,7 @@ struct Foo {
117} 118}
118 119
119impl Debug for Foo { 120impl Debug for Foo {
120<|> 121 $0
121} 122}
122 ", 123 ",
123 ) 124 )
@@ -139,7 +140,7 @@ pub struct Foo {
139} 140}
140 141
141impl Debug for Foo { 142impl Debug for Foo {
142<|> 143 $0
143} 144}
144 ", 145 ",
145 ) 146 )
@@ -158,7 +159,7 @@ struct Foo {}
158struct Foo {} 159struct Foo {}
159 160
160impl Debug for Foo { 161impl Debug for Foo {
161<|> 162 $0
162} 163}
163 ", 164 ",
164 ) 165 )
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index fb08c19e9..b123b8498 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -18,31 +18,37 @@ use crate::{AssistContext, AssistId, Assists};
18// ``` 18// ```
19// -> 19// ->
20// ``` 20// ```
21// #[derive()] 21// #[derive($0)]
22// struct Point { 22// struct Point {
23// x: u32, 23// x: u32,
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?;
28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
29 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
30 let target = nominal.syntax().text_range(); 31 let target = nominal.syntax().text_range();
31 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { 32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| {
32 let derive_attr = nominal 33 let derive_attr = nominal
33 .attrs() 34 .attrs()
34 .filter_map(|x| x.as_simple_call()) 35 .filter_map(|x| x.as_simple_call())
35 .filter(|(name, _arg)| name == "derive") 36 .filter(|(name, _arg)| name == "derive")
36 .map(|(_name, arg)| arg) 37 .map(|(_name, arg)| arg)
37 .next(); 38 .next();
38 let offset = match derive_attr { 39 match derive_attr {
39 None => { 40 None => {
40 edit.insert(node_start, "#[derive()]\n"); 41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
41 node_start + TextSize::of("#[derive(") 42 }
43 Some(tt) => {
44 // Just move the cursor.
45 builder.insert_snippet(
46 cap,
47 tt.syntax().text_range().end() - TextSize::of(')'),
48 "$0",
49 )
42 } 50 }
43 Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'),
44 }; 51 };
45 edit.set_cursor(offset)
46 }) 52 })
47} 53}
48 54
@@ -66,12 +72,12 @@ mod tests {
66 check_assist( 72 check_assist(
67 add_derive, 73 add_derive,
68 "struct Foo { a: i32, <|>}", 74 "struct Foo { a: i32, <|>}",
69 "#[derive(<|>)]\nstruct Foo { a: i32, }", 75 "#[derive($0)]\nstruct Foo { a: i32, }",
70 ); 76 );
71 check_assist( 77 check_assist(
72 add_derive, 78 add_derive,
73 "struct Foo { <|> a: i32, }", 79 "struct Foo { <|> a: i32, }",
74 "#[derive(<|>)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
75 ); 81 );
76 } 82 }
77 83
@@ -80,7 +86,7 @@ mod tests {
80 check_assist( 86 check_assist(
81 add_derive, 87 add_derive,
82 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
83 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", 89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
84 ); 90 );
85 } 91 }
86 92
@@ -96,7 +102,7 @@ struct Foo { a: i32<|>, }
96 " 102 "
97/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
98/// It does stuff. 104/// It does stuff.
99#[derive(<|>)] 105#[derive($0)]
100struct Foo { a: i32, } 106struct Foo { a: i32, }
101 ", 107 ",
102 ); 108 );
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 0c7d5e355..ab20c6649 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
25 let stmt = ctx.find_node_at_offset::<LetStmt>()?; 25 let stmt = ctx.find_node_at_offset::<LetStmt>()?;
26 let module = ctx.sema.scope(stmt.syntax()).module()?; 26 let module = ctx.sema.scope(stmt.syntax()).module()?;
27 let expr = stmt.initializer()?; 27 let expr = stmt.initializer()?;
28 let pat = stmt.pat()?;
29 // Must be a binding 28 // Must be a binding
30 let pat = match pat { 29 let pat = match stmt.pat()? {
31 ast::Pat::BindPat(bind_pat) => bind_pat, 30 ast::Pat::BindPat(bind_pat) => bind_pat,
32 _ => return None, 31 _ => return None,
33 }; 32 };
@@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
46 // Assist not applicable if the type has already been specified 45 // Assist not applicable if the type has already been specified
47 // and it has no placeholders 46 // and it has no placeholders
48 let ascribed_ty = stmt.ascribed_type(); 47 let ascribed_ty = stmt.ascribed_type();
49 if let Some(ref ty) = ascribed_ty { 48 if let Some(ty) = &ascribed_ty {
50 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { 49 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
51 return None; 50 return None;
52 } 51 }
@@ -87,11 +86,7 @@ mod tests {
87 86
88 #[test] 87 #[test]
89 fn add_explicit_type_works_for_simple_expr() { 88 fn add_explicit_type_works_for_simple_expr() {
90 check_assist( 89 check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }");
91 add_explicit_type,
92 "fn f() { let a<|> = 1; }",
93 "fn f() { let a<|>: i32 = 1; }",
94 );
95 } 90 }
96 91
97 #[test] 92 #[test]
@@ -99,7 +94,7 @@ mod tests {
99 check_assist( 94 check_assist(
100 add_explicit_type, 95 add_explicit_type,
101 "fn f() { let a<|>: _ = 1; }", 96 "fn f() { let a<|>: _ = 1; }",
102 "fn f() { let a<|>: i32 = 1; }", 97 "fn f() { let a: i32 = 1; }",
103 ); 98 );
104 } 99 }
105 100
@@ -123,7 +118,7 @@ mod tests {
123 } 118 }
124 119
125 fn f() { 120 fn f() {
126 let a<|>: Option<i32> = Option::Some(1); 121 let a: Option<i32> = Option::Some(1);
127 }"#, 122 }"#,
128 ); 123 );
129 } 124 }
@@ -133,7 +128,7 @@ mod tests {
133 check_assist( 128 check_assist(
134 add_explicit_type, 129 add_explicit_type,
135 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", 130 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
136 r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", 131 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
137 ); 132 );
138 } 133 }
139 134
@@ -141,8 +136,8 @@ mod tests {
141 fn add_explicit_type_works_for_macro_call_recursive() { 136 fn add_explicit_type_works_for_macro_call_recursive() {
142 check_assist( 137 check_assist(
143 add_explicit_type, 138 add_explicit_type,
144 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", 139 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#,
145 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", 140 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
146 ); 141 );
147 } 142 }
148 143
@@ -209,7 +204,7 @@ struct Test<K, T = u8> {
209} 204}
210 205
211fn main() { 206fn main() {
212 let test<|>: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23, k: 33 };
213}"#, 208}"#,
214 ); 209 );
215 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 6a49b7dbd..6a675e812 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -1,7 +1,6 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner}; 2use ra_syntax::ast::{self, AstNode, NameOwner};
3use stdx::format_to; 3use test_utils::mark;
4use test_utils::tested_by;
5 4
6use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
7 6
@@ -35,12 +34,12 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
35 } 34 }
36 let field_type = field_list.fields().next()?.type_ref()?; 35 let field_type = field_list.fields().next()?.type_ref()?;
37 let path = match field_type { 36 let path = match field_type {
38 ast::TypeRef::PathType(p) => p, 37 ast::TypeRef::PathType(it) => it,
39 _ => return None, 38 _ => return None,
40 }; 39 };
41 40
42 if existing_from_impl(&ctx.sema, &variant).is_some() { 41 if existing_from_impl(&ctx.sema, &variant).is_some() {
43 tested_by!(test_add_from_impl_already_exists); 42 mark::hit!(test_add_from_impl_already_exists);
44 return None; 43 return None;
45 } 44 }
46 45
@@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
51 target, 50 target,
52 |edit| { 51 |edit| {
53 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
54 let mut buf = String::new(); 53 let buf = format!(
55 format_to!(
56 buf,
57 r#" 54 r#"
58 55
59impl From<{0}> for {1} {{ 56impl From<{0}> for {1} {{
@@ -93,7 +90,7 @@ fn existing_from_impl(
93 90
94#[cfg(test)] 91#[cfg(test)]
95mod tests { 92mod tests {
96 use test_utils::covers; 93 use test_utils::mark;
97 94
98 use crate::tests::{check_assist, check_assist_not_applicable}; 95 use crate::tests::{check_assist, check_assist_not_applicable};
99 96
@@ -104,7 +101,7 @@ mod tests {
104 check_assist( 101 check_assist(
105 add_from_impl_for_enum, 102 add_from_impl_for_enum,
106 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
107 r#"enum A { <|>One(u32) } 104 r#"enum A { One(u32) }
108 105
109impl From<u32> for A { 106impl From<u32> for A {
110 fn from(v: u32) -> Self { 107 fn from(v: u32) -> Self {
@@ -119,7 +116,7 @@ impl From<u32> for A {
119 check_assist( 116 check_assist(
120 add_from_impl_for_enum, 117 add_from_impl_for_enum,
121 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
122 r#"enum A { <|>One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
123 120
124impl From<foo::bar::baz::Boo> for A { 121impl From<foo::bar::baz::Boo> for A {
125 fn from(v: foo::bar::baz::Boo) -> Self { 122 fn from(v: foo::bar::baz::Boo) -> Self {
@@ -152,7 +149,7 @@ impl From<foo::bar::baz::Boo> for A {
152 149
153 #[test] 150 #[test]
154 fn test_add_from_impl_already_exists() { 151 fn test_add_from_impl_already_exists() {
155 covers!(test_add_from_impl_already_exists); 152 mark::check!(test_add_from_impl_already_exists);
156 check_not_applicable( 153 check_not_applicable(
157 r#" 154 r#"
158enum A { <|>One(u32), } 155enum A { <|>One(u32), }
@@ -181,7 +178,7 @@ impl From<String> for A {
181pub trait From<T> { 178pub trait From<T> {
182 fn from(T) -> Self; 179 fn from(T) -> Self;
183}"#, 180}"#,
184 r#"enum A { <|>One(u32), Two(String), } 181 r#"enum A { One(u32), Two(String), }
185 182
186impl From<u32> for A { 183impl From<u32> for A {
187 fn from(v: u32) -> Self { 184 fn from(v: u32) -> Self {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index de016ae4e..24f931a85 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -4,13 +4,17 @@ use ra_syntax::{
4 ast::{ 4 ast::{
5 self, 5 self,
6 edit::{AstNodeEdit, IndentLevel}, 6 edit::{AstNodeEdit, IndentLevel},
7 ArgListOwner, AstNode, ModuleItemOwner, 7 make, ArgListOwner, AstNode, ModuleItemOwner,
8 }, 8 },
9 SyntaxKind, SyntaxNode, TextSize, 9 SyntaxKind, SyntaxNode, TextSize,
10}; 10};
11use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap, FxHashSet};
12 12
13use crate::{AssistContext, AssistId, Assists}; 13use crate::{
14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists,
17};
14 18
15// Assist: add_function 19// Assist: add_function
16// 20//
@@ -33,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists};
33// } 37// }
34// 38//
35// fn bar(arg: &str, baz: Baz) { 39// fn bar(arg: &str, baz: Baz) {
36// todo!() 40// ${0:todo!()}
37// } 41// }
38// 42//
39// ``` 43// ```
@@ -58,21 +62,40 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
58 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
59 63
60 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
61 acc.add(AssistId("add_function"), "Add function", target, |edit| { 65 acc.add(AssistId("add_function"), "Add function", target, |builder| {
62 let function_template = function_builder.render(); 66 let function_template = function_builder.render();
63 edit.set_file(function_template.file); 67 builder.set_file(function_template.file);
64 edit.set_cursor(function_template.cursor_offset); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
65 edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); 69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
71 None => builder.insert(function_template.insert_offset, new_fn),
72 }
66 }) 73 })
67} 74}
68 75
69struct FunctionTemplate { 76struct FunctionTemplate {
70 insert_offset: TextSize, 77 insert_offset: TextSize,
71 cursor_offset: TextSize, 78 placeholder_expr: ast::MacroCall,
72 fn_def: ast::SourceFile, 79 leading_ws: String,
80 fn_def: ast::FnDef,
81 trailing_ws: String,
73 file: FileId, 82 file: FileId,
74} 83}
75 84
85impl FunctionTemplate {
86 fn to_string(&self, cap: Option<SnippetCap>) -> String {
87 let f = match cap {
88 Some(cap) => render_snippet(
89 cap,
90 self.fn_def.syntax(),
91 Cursor::Replace(self.placeholder_expr.syntax()),
92 ),
93 None => self.fn_def.to_string(),
94 };
95 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
96 }
97}
98
76struct FunctionBuilder { 99struct FunctionBuilder {
77 target: GeneratedFunctionTarget, 100 target: GeneratedFunctionTarget,
78 fn_name: ast::Name, 101 fn_name: ast::Name,
@@ -110,35 +133,41 @@ impl FunctionBuilder {
110 } 133 }
111 134
112 fn render(self) -> FunctionTemplate { 135 fn render(self) -> FunctionTemplate {
113 let placeholder_expr = ast::make::expr_todo(); 136 let placeholder_expr = make::expr_todo();
114 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 137 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
115 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 138 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
116 if self.needs_pub { 139 let mut fn_def =
117 fn_def = ast::make::add_pub_crate_modifier(fn_def); 140 make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body);
118 } 141 let leading_ws;
119 142 let trailing_ws;
120 let (fn_def, insert_offset) = match self.target { 143
144 let insert_offset = match self.target {
121 GeneratedFunctionTarget::BehindItem(it) => { 145 GeneratedFunctionTarget::BehindItem(it) => {
122 let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); 146 let indent = IndentLevel::from_node(&it);
123 let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); 147 leading_ws = format!("\n\n{}", indent);
124 (indented, it.text_range().end()) 148 fn_def = fn_def.indent(indent);
149 trailing_ws = String::new();
150 it.text_range().end()
125 } 151 }
126 GeneratedFunctionTarget::InEmptyItemList(it) => { 152 GeneratedFunctionTarget::InEmptyItemList(it) => {
127 let indent_once = IndentLevel(1);
128 let indent = IndentLevel::from_node(it.syntax()); 153 let indent = IndentLevel::from_node(it.syntax());
129 let fn_def = ast::make::add_leading_newlines(1, fn_def); 154 leading_ws = format!("\n{}", indent + 1);
130 let fn_def = fn_def.indent(indent_once); 155 fn_def = fn_def.indent(indent + 1);
131 let fn_def = ast::make::add_trailing_newlines(1, fn_def); 156 trailing_ws = format!("\n{}", indent);
132 let fn_def = fn_def.indent(indent); 157 it.syntax().text_range().start() + TextSize::of('{')
133 (fn_def, it.syntax().text_range().start() + TextSize::of('{'))
134 } 158 }
135 }; 159 };
136 160
137 let placeholder_expr = 161 let placeholder_expr =
138 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); 162 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
139 let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start(); 163 FunctionTemplate {
140 let cursor_offset = insert_offset + cursor_offset_from_fn_start; 164 insert_offset,
141 FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file } 165 placeholder_expr,
166 leading_ws,
167 fn_def,
168 trailing_ws,
169 file: self.file,
170 }
142 } 171 }
143} 172}
144 173
@@ -158,7 +187,7 @@ impl GeneratedFunctionTarget {
158 187
159fn fn_name(call: &ast::Path) -> Option<ast::Name> { 188fn fn_name(call: &ast::Path) -> Option<ast::Name> {
160 let name = call.segment()?.syntax().to_string(); 189 let name = call.segment()?.syntax().to_string();
161 Some(ast::make::name(&name)) 190 Some(make::name(&name))
162} 191}
163 192
164/// Computes the type variables and arguments required for the generated function 193/// Computes the type variables and arguments required for the generated function
@@ -180,8 +209,8 @@ fn fn_args(
180 }); 209 });
181 } 210 }
182 deduplicate_arg_names(&mut arg_names); 211 deduplicate_arg_names(&mut arg_names);
183 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); 212 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty));
184 Some((None, ast::make::param_list(params))) 213 Some((None, make::param_list(params)))
185} 214}
186 215
187/// Makes duplicate argument names unique by appending incrementing numbers. 216/// Makes duplicate argument names unique by appending incrementing numbers.
@@ -316,7 +345,7 @@ fn foo() {
316} 345}
317 346
318fn bar() { 347fn bar() {
319 <|>todo!() 348 ${0:todo!()}
320} 349}
321", 350",
322 ) 351 )
@@ -343,7 +372,7 @@ impl Foo {
343} 372}
344 373
345fn bar() { 374fn bar() {
346 <|>todo!() 375 ${0:todo!()}
347} 376}
348", 377",
349 ) 378 )
@@ -367,7 +396,7 @@ fn foo1() {
367} 396}
368 397
369fn bar() { 398fn bar() {
370 <|>todo!() 399 ${0:todo!()}
371} 400}
372 401
373fn foo2() {} 402fn foo2() {}
@@ -393,7 +422,7 @@ mod baz {
393 } 422 }
394 423
395 fn bar() { 424 fn bar() {
396 <|>todo!() 425 ${0:todo!()}
397 } 426 }
398} 427}
399", 428",
@@ -419,7 +448,7 @@ fn foo() {
419} 448}
420 449
421fn bar(baz: Baz) { 450fn bar(baz: Baz) {
422 <|>todo!() 451 ${0:todo!()}
423} 452}
424", 453",
425 ); 454 );
@@ -452,7 +481,7 @@ impl Baz {
452} 481}
453 482
454fn bar(baz: Baz) { 483fn bar(baz: Baz) {
455 <|>todo!() 484 ${0:todo!()}
456} 485}
457", 486",
458 ) 487 )
@@ -473,7 +502,7 @@ fn foo() {
473} 502}
474 503
475fn bar(arg: &str) { 504fn bar(arg: &str) {
476 <|>todo!() 505 ${0:todo!()}
477} 506}
478"#, 507"#,
479 ) 508 )
@@ -494,7 +523,7 @@ fn foo() {
494} 523}
495 524
496fn bar(arg: char) { 525fn bar(arg: char) {
497 <|>todo!() 526 ${0:todo!()}
498} 527}
499"#, 528"#,
500 ) 529 )
@@ -515,7 +544,7 @@ fn foo() {
515} 544}
516 545
517fn bar(arg: i32) { 546fn bar(arg: i32) {
518 <|>todo!() 547 ${0:todo!()}
519} 548}
520", 549",
521 ) 550 )
@@ -536,7 +565,7 @@ fn foo() {
536} 565}
537 566
538fn bar(arg: u8) { 567fn bar(arg: u8) {
539 <|>todo!() 568 ${0:todo!()}
540} 569}
541", 570",
542 ) 571 )
@@ -561,7 +590,7 @@ fn foo() {
561} 590}
562 591
563fn bar(x: u8) { 592fn bar(x: u8) {
564 <|>todo!() 593 ${0:todo!()}
565} 594}
566", 595",
567 ) 596 )
@@ -584,7 +613,7 @@ fn foo() {
584} 613}
585 614
586fn bar(worble: ()) { 615fn bar(worble: ()) {
587 <|>todo!() 616 ${0:todo!()}
588} 617}
589", 618",
590 ) 619 )
@@ -613,7 +642,7 @@ fn baz() {
613} 642}
614 643
615fn bar(foo: impl Foo) { 644fn bar(foo: impl Foo) {
616 <|>todo!() 645 ${0:todo!()}
617} 646}
618", 647",
619 ) 648 )
@@ -640,7 +669,7 @@ fn foo() {
640} 669}
641 670
642fn bar(baz: &Baz) { 671fn bar(baz: &Baz) {
643 <|>todo!() 672 ${0:todo!()}
644} 673}
645", 674",
646 ) 675 )
@@ -669,7 +698,7 @@ fn foo() {
669} 698}
670 699
671fn bar(baz: Baz::Bof) { 700fn bar(baz: Baz::Bof) {
672 <|>todo!() 701 ${0:todo!()}
673} 702}
674", 703",
675 ) 704 )
@@ -692,7 +721,7 @@ fn foo<T>(t: T) {
692} 721}
693 722
694fn bar<T>(t: T) { 723fn bar<T>(t: T) {
695 <|>todo!() 724 ${0:todo!()}
696} 725}
697", 726",
698 ) 727 )
@@ -723,7 +752,7 @@ fn foo() {
723} 752}
724 753
725fn bar(arg: fn() -> Baz) { 754fn bar(arg: fn() -> Baz) {
726 <|>todo!() 755 ${0:todo!()}
727} 756}
728", 757",
729 ) 758 )
@@ -748,7 +777,7 @@ fn foo() {
748} 777}
749 778
750fn bar(closure: impl Fn(i64) -> i64) { 779fn bar(closure: impl Fn(i64) -> i64) {
751 <|>todo!() 780 ${0:todo!()}
752} 781}
753", 782",
754 ) 783 )
@@ -769,7 +798,7 @@ fn foo() {
769} 798}
770 799
771fn bar(baz: ()) { 800fn bar(baz: ()) {
772 <|>todo!() 801 ${0:todo!()}
773} 802}
774", 803",
775 ) 804 )
@@ -794,7 +823,7 @@ fn foo() {
794} 823}
795 824
796fn bar(baz_1: Baz, baz_2: Baz) { 825fn bar(baz_1: Baz, baz_2: Baz) {
797 <|>todo!() 826 ${0:todo!()}
798} 827}
799", 828",
800 ) 829 )
@@ -819,7 +848,7 @@ fn foo() {
819} 848}
820 849
821fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { 850fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
822 <|>todo!() 851 ${0:todo!()}
823} 852}
824"#, 853"#,
825 ) 854 )
@@ -839,7 +868,7 @@ fn foo() {
839 r" 868 r"
840mod bar { 869mod bar {
841 pub(crate) fn my_fn() { 870 pub(crate) fn my_fn() {
842 <|>todo!() 871 ${0:todo!()}
843 } 872 }
844} 873}
845 874
@@ -878,7 +907,7 @@ fn bar() {
878} 907}
879 908
880fn baz(foo: foo::Foo) { 909fn baz(foo: foo::Foo) {
881 <|>todo!() 910 ${0:todo!()}
882} 911}
883", 912",
884 ) 913 )
@@ -902,7 +931,7 @@ mod bar {
902 fn something_else() {} 931 fn something_else() {}
903 932
904 pub(crate) fn my_fn() { 933 pub(crate) fn my_fn() {
905 <|>todo!() 934 ${0:todo!()}
906 } 935 }
907} 936}
908 937
@@ -930,7 +959,7 @@ fn foo() {
930mod bar { 959mod bar {
931 mod baz { 960 mod baz {
932 pub(crate) fn my_fn() { 961 pub(crate) fn my_fn() {
933 <|>todo!() 962 ${0:todo!()}
934 } 963 }
935 } 964 }
936} 965}
@@ -959,7 +988,7 @@ fn main() {
959 988
960 989
961pub(crate) fn bar() { 990pub(crate) fn bar() {
962 <|>todo!() 991 ${0:todo!()}
963}", 992}",
964 ) 993 )
965 } 994 }
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index df114a0d8..eceba7d0a 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -1,7 +1,4 @@
1use ra_syntax::{ 1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2 ast::{self, AstNode, NameOwner, TypeParamsOwner},
3 TextSize,
4};
5use stdx::{format_to, SepBy}; 2use stdx::{format_to, SepBy};
6 3
7use crate::{AssistContext, AssistId, Assists}; 4use crate::{AssistContext, AssistId, Assists};
@@ -12,17 +9,17 @@ use crate::{AssistContext, AssistId, Assists};
12// 9//
13// ``` 10// ```
14// struct Ctx<T: Clone> { 11// struct Ctx<T: Clone> {
15// data: T,<|> 12// data: T,<|>
16// } 13// }
17// ``` 14// ```
18// -> 15// ->
19// ``` 16// ```
20// struct Ctx<T: Clone> { 17// struct Ctx<T: Clone> {
21// data: T, 18// data: T,
22// } 19// }
23// 20//
24// impl<T: Clone> Ctx<T> { 21// impl<T: Clone> Ctx<T> {
25// 22// $0
26// } 23// }
27// ``` 24// ```
28pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -50,30 +47,37 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
50 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
51 format_to!(buf, "<{}>", generic_params) 48 format_to!(buf, "<{}>", generic_params)
52 } 49 }
53 buf.push_str(" {\n"); 50 match ctx.config.snippet_cap {
54 edit.set_cursor(start_offset + TextSize::of(&buf)); 51 Some(cap) => {
55 buf.push_str("\n}"); 52 buf.push_str(" {\n $0\n}");
56 edit.insert(start_offset, buf); 53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
57 }) 60 })
58} 61}
59 62
60#[cfg(test)] 63#[cfg(test)]
61mod tests { 64mod tests {
62 use super::*;
63 use crate::tests::{check_assist, check_assist_target}; 65 use crate::tests::{check_assist, check_assist_target};
64 66
67 use super::*;
68
65 #[test] 69 #[test]
66 fn test_add_impl() { 70 fn test_add_impl() {
67 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n"); 71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
68 check_assist( 72 check_assist(
69 add_impl, 73 add_impl,
70 "struct Foo<T: Clone> {<|>}", 74 "struct Foo<T: Clone> {<|>}",
71 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", 75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
72 ); 76 );
73 check_assist( 77 check_assist(
74 add_impl, 78 add_impl,
75 "struct Foo<'a, T: Foo<'a>> {<|>}", 79 "struct Foo<'a, T: Foo<'a>> {<|>}",
76 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", 80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
77 ); 81 );
78 } 82 }
79 83
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 22e1156d2..abacd4065 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use crate::{ 11use 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, resolve_target_trait}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, 15 AssistId,
16}; 16};
17 17
@@ -46,7 +46,7 @@ enum AddMissingImplMembersMode {
46// 46//
47// impl Trait<u32> for () { 47// impl Trait<u32> for () {
48// fn foo(&self) -> u32 { 48// fn foo(&self) -> u32 {
49// todo!() 49// ${0:todo!()}
50// } 50// }
51// 51//
52// } 52// }
@@ -89,7 +89,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
89// impl Trait for () { 89// impl Trait for () {
90// Type X = (); 90// Type X = ();
91// fn foo(&self) {} 91// fn foo(&self) {}
92// fn bar(&self) {} 92// $0fn bar(&self) {}
93// 93//
94// } 94// }
95// ``` 95// ```
@@ -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, |edit| { 150 acc.add(AssistId(assist_id), 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());
@@ -162,13 +162,29 @@ fn add_missing_impl_members_inner(
162 }) 162 })
163 .map(|it| edit::remove_attrs_and_docs(&it)); 163 .map(|it| edit::remove_attrs_and_docs(&it));
164 let new_impl_item_list = impl_item_list.append_items(items); 164 let new_impl_item_list = impl_item_list.append_items(items);
165 let cursor_position = { 165 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
166 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); 166
167 first_new_item.syntax().text_range().start() 167 let original_range = impl_item_list.syntax().text_range();
168 match ctx.config.snippet_cap {
169 None => builder.replace(original_range, new_impl_item_list.to_string()),
170 Some(cap) => {
171 let mut cursor = Cursor::Before(first_new_item.syntax());
172 let placeholder;
173 if let ast::AssocItem::FnDef(func) = &first_new_item {
174 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
175 if m.syntax().text() == "todo!()" {
176 placeholder = m;
177 cursor = Cursor::Replace(placeholder.syntax());
178 }
179 }
180 }
181 builder.replace_snippet(
182 cap,
183 original_range,
184 render_snippet(cap, new_impl_item_list.syntax(), cursor),
185 )
186 }
168 }; 187 };
169
170 edit.replace_ast(impl_item_list, new_impl_item_list);
171 edit.set_cursor(cursor_position);
172 }) 188 })
173} 189}
174 190
@@ -222,7 +238,7 @@ struct S;
222 238
223impl Foo for S { 239impl Foo for S {
224 fn bar(&self) {} 240 fn bar(&self) {}
225 <|>type Output; 241 $0type Output;
226 const CONST: usize = 42; 242 const CONST: usize = 42;
227 fn foo(&self) { 243 fn foo(&self) {
228 todo!() 244 todo!()
@@ -263,8 +279,8 @@ struct S;
263 279
264impl Foo for S { 280impl Foo for S {
265 fn bar(&self) {} 281 fn bar(&self) {}
266 <|>fn foo(&self) { 282 fn foo(&self) {
267 todo!() 283 ${0:todo!()}
268 } 284 }
269 285
270}"#, 286}"#,
@@ -283,8 +299,8 @@ impl Foo for S { <|> }"#,
283trait Foo { fn foo(&self); } 299trait Foo { fn foo(&self); }
284struct S; 300struct S;
285impl Foo for S { 301impl Foo for S {
286 <|>fn foo(&self) { 302 fn foo(&self) {
287 todo!() 303 ${0:todo!()}
288 } 304 }
289}"#, 305}"#,
290 ); 306 );
@@ -302,8 +318,8 @@ impl Foo<u32> for S { <|> }"#,
302trait Foo<T> { fn foo(&self, t: T) -> &T; } 318trait Foo<T> { fn foo(&self, t: T) -> &T; }
303struct S; 319struct S;
304impl Foo<u32> for S { 320impl Foo<u32> for S {
305 <|>fn foo(&self, t: u32) -> &u32 { 321 fn foo(&self, t: u32) -> &u32 {
306 todo!() 322 ${0:todo!()}
307 } 323 }
308}"#, 324}"#,
309 ); 325 );
@@ -321,8 +337,8 @@ impl<U> Foo<U> for S { <|> }"#,
321trait Foo<T> { fn foo(&self, t: T) -> &T; } 337trait Foo<T> { fn foo(&self, t: T) -> &T; }
322struct S; 338struct S;
323impl<U> Foo<U> for S { 339impl<U> Foo<U> for S {
324 <|>fn foo(&self, t: U) -> &U { 340 fn foo(&self, t: U) -> &U {
325 todo!() 341 ${0:todo!()}
326 } 342 }
327}"#, 343}"#,
328 ); 344 );
@@ -340,8 +356,8 @@ impl Foo for S {}<|>"#,
340trait Foo { fn foo(&self); } 356trait Foo { fn foo(&self); }
341struct S; 357struct S;
342impl Foo for S { 358impl Foo for S {
343 <|>fn foo(&self) { 359 fn foo(&self) {
344 todo!() 360 ${0:todo!()}
345 } 361 }
346}"#, 362}"#,
347 ) 363 )
@@ -365,8 +381,8 @@ mod foo {
365} 381}
366struct S; 382struct S;
367impl foo::Foo for S { 383impl foo::Foo for S {
368 <|>fn foo(&self, bar: foo::Bar) { 384 fn foo(&self, bar: foo::Bar) {
369 todo!() 385 ${0:todo!()}
370 } 386 }
371}"#, 387}"#,
372 ); 388 );
@@ -390,8 +406,8 @@ mod foo {
390} 406}
391struct S; 407struct S;
392impl foo::Foo for S { 408impl foo::Foo for S {
393 <|>fn foo(&self, bar: foo::Bar<u32>) { 409 fn foo(&self, bar: foo::Bar<u32>) {
394 todo!() 410 ${0:todo!()}
395 } 411 }
396}"#, 412}"#,
397 ); 413 );
@@ -415,8 +431,8 @@ mod foo {
415} 431}
416struct S; 432struct S;
417impl foo::Foo<u32> for S { 433impl foo::Foo<u32> for S {
418 <|>fn foo(&self, bar: foo::Bar<u32>) { 434 fn foo(&self, bar: foo::Bar<u32>) {
419 todo!() 435 ${0:todo!()}
420 } 436 }
421}"#, 437}"#,
422 ); 438 );
@@ -443,8 +459,8 @@ mod foo {
443struct Param; 459struct Param;
444struct S; 460struct S;
445impl foo::Foo<Param> for S { 461impl foo::Foo<Param> for S {
446 <|>fn foo(&self, bar: Param) { 462 fn foo(&self, bar: Param) {
447 todo!() 463 ${0:todo!()}
448 } 464 }
449}"#, 465}"#,
450 ); 466 );
@@ -470,8 +486,8 @@ mod foo {
470} 486}
471struct S; 487struct S;
472impl foo::Foo for S { 488impl foo::Foo for S {
473 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { 489 fn foo(&self, bar: foo::Bar<u32>::Assoc) {
474 todo!() 490 ${0:todo!()}
475 } 491 }
476}"#, 492}"#,
477 ); 493 );
@@ -497,8 +513,8 @@ mod foo {
497} 513}
498struct S; 514struct S;
499impl foo::Foo for S { 515impl foo::Foo for S {
500 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { 516 fn foo(&self, bar: foo::Bar<foo::Baz>) {
501 todo!() 517 ${0:todo!()}
502 } 518 }
503}"#, 519}"#,
504 ); 520 );
@@ -522,8 +538,8 @@ mod foo {
522} 538}
523struct S; 539struct S;
524impl foo::Foo for S { 540impl foo::Foo for S {
525 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { 541 fn foo(&self, bar: dyn Fn(u32) -> i32) {
526 todo!() 542 ${0:todo!()}
527 } 543 }
528}"#, 544}"#,
529 ); 545 );
@@ -580,7 +596,7 @@ trait Foo {
580} 596}
581struct S; 597struct S;
582impl Foo for S { 598impl Foo for S {
583 <|>type Output; 599 $0type Output;
584 fn foo(&self) { 600 fn foo(&self) {
585 todo!() 601 todo!()
586 } 602 }
@@ -614,7 +630,7 @@ trait Foo {
614} 630}
615struct S; 631struct S;
616impl Foo for S { 632impl Foo for S {
617 <|>fn valid(some: u32) -> bool { false } 633 $0fn valid(some: u32) -> bool { false }
618}"#, 634}"#,
619 ) 635 )
620 } 636 }
@@ -637,8 +653,8 @@ trait Foo<T = Self> {
637 653
638struct S; 654struct S;
639impl Foo for S { 655impl Foo for S {
640 <|>fn bar(&self, other: &Self) { 656 fn bar(&self, other: &Self) {
641 todo!() 657 ${0:todo!()}
642 } 658 }
643}"#, 659}"#,
644 ) 660 )
@@ -662,8 +678,8 @@ trait Foo<T1, T2 = Self> {
662 678
663struct S<T>; 679struct S<T>;
664impl Foo<T> for S<T> { 680impl Foo<T> for S<T> {
665 <|>fn bar(&self, this: &T, that: &Self) { 681 fn bar(&self, this: &T, that: &Self) {
666 todo!() 682 ${0:todo!()}
667 } 683 }
668}"#, 684}"#,
669 ) 685 )
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index fe7451dcf..837aa8377 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 ast::{ 3 ast::{
4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, 4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
5 }, 5 },
6 TextSize, T, 6 T,
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
25// } 25// }
26// 26//
27// impl<T: Clone> Ctx<T> { 27// impl<T: Clone> Ctx<T> {
28// fn new(data: T) -> Self { Self { data } } 28// fn $0new(data: T) -> Self { Self { data } }
29// } 29// }
30// 30//
31// ``` 31// ```
@@ -42,31 +42,26 @@ 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, |edit| { 45 acc.add(AssistId("add_new"), "Add default constructor", 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() {
49 buf.push('\n'); 49 buf.push('\n');
50 } 50 }
51 51
52 let vis = strukt.visibility().map(|v| format!("{} ", v)); 52 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
53 let vis = vis.as_deref().unwrap_or("");
54 53
55 let params = field_list 54 let params = field_list
56 .fields() 55 .fields()
57 .filter_map(|f| { 56 .filter_map(|f| {
58 Some(format!( 57 Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
59 "{}: {}",
60 f.name()?.syntax().text(),
61 f.ascribed_type()?.syntax().text()
62 ))
63 }) 58 })
64 .sep_by(", "); 59 .sep_by(", ");
65 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); 60 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
66 61
67 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); 62 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
68 63
69 let (start_offset, end_offset) = impl_def 64 let start_offset = impl_def
70 .and_then(|impl_def| { 65 .and_then(|impl_def| {
71 buf.push('\n'); 66 buf.push('\n');
72 let start = impl_def 67 let start = impl_def
@@ -76,17 +71,20 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
76 .text_range() 71 .text_range()
77 .end(); 72 .end();
78 73
79 Some((start, TextSize::of("\n"))) 74 Some(start)
80 }) 75 })
81 .unwrap_or_else(|| { 76 .unwrap_or_else(|| {
82 buf = generate_impl_text(&strukt, &buf); 77 buf = generate_impl_text(&strukt, &buf);
83 let start = strukt.syntax().text_range().end(); 78 strukt.syntax().text_range().end()
84
85 (start, TextSize::of("\n}\n"))
86 }); 79 });
87 80
88 edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset); 81 match ctx.config.snippet_cap {
89 edit.insert(start_offset, buf); 82 None => builder.insert(start_offset, buf),
83 Some(cap) => {
84 buf = buf.replace("fn new", "fn $0new");
85 builder.insert_snippet(cap, start_offset, buf);
86 }
87 }
90 }) 88 })
91} 89}
92 90
@@ -191,7 +189,7 @@ mod tests {
191"struct Foo {} 189"struct Foo {}
192 190
193impl Foo { 191impl Foo {
194 fn new() -> Self { Self { } }<|> 192 fn $0new() -> Self { Self { } }
195} 193}
196", 194",
197 ); 195 );
@@ -201,7 +199,7 @@ impl Foo {
201"struct Foo<T: Clone> {} 199"struct Foo<T: Clone> {}
202 200
203impl<T: Clone> Foo<T> { 201impl<T: Clone> Foo<T> {
204 fn new() -> Self { Self { } }<|> 202 fn $0new() -> Self { Self { } }
205} 203}
206", 204",
207 ); 205 );
@@ -211,7 +209,7 @@ impl<T: Clone> Foo<T> {
211"struct Foo<'a, T: Foo<'a>> {} 209"struct Foo<'a, T: Foo<'a>> {}
212 210
213impl<'a, T: Foo<'a>> Foo<'a, T> { 211impl<'a, T: Foo<'a>> Foo<'a, T> {
214 fn new() -> Self { Self { } }<|> 212 fn $0new() -> Self { Self { } }
215} 213}
216", 214",
217 ); 215 );
@@ -221,7 +219,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
221"struct Foo { baz: String } 219"struct Foo { baz: String }
222 220
223impl Foo { 221impl Foo {
224 fn new(baz: String) -> Self { Self { baz } }<|> 222 fn $0new(baz: String) -> Self { Self { baz } }
225} 223}
226", 224",
227 ); 225 );
@@ -231,7 +229,7 @@ impl Foo {
231"struct Foo { baz: String, qux: Vec<i32> } 229"struct Foo { baz: String, qux: Vec<i32> }
232 230
233impl Foo { 231impl Foo {
234 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> 232 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
235} 233}
236", 234",
237 ); 235 );
@@ -243,7 +241,7 @@ impl Foo {
243"struct Foo { pub baz: String, pub qux: Vec<i32> } 241"struct Foo { pub baz: String, pub qux: Vec<i32> }
244 242
245impl Foo { 243impl Foo {
246 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> 244 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
247} 245}
248", 246",
249 ); 247 );
@@ -258,7 +256,7 @@ impl Foo {}
258"struct Foo {} 256"struct Foo {}
259 257
260impl Foo { 258impl Foo {
261 fn new() -> Self { Self { } }<|> 259 fn $0new() -> Self { Self { } }
262} 260}
263", 261",
264 ); 262 );
@@ -273,7 +271,7 @@ impl Foo {
273"struct Foo {} 271"struct Foo {}
274 272
275impl Foo { 273impl Foo {
276 fn new() -> Self { Self { } }<|> 274 fn $0new() -> Self { Self { } }
277 275
278 fn qux(&self) {} 276 fn qux(&self) {}
279} 277}
@@ -294,7 +292,7 @@ impl Foo {
294"struct Foo {} 292"struct Foo {}
295 293
296impl Foo { 294impl Foo {
297 fn new() -> Self { Self { } }<|> 295 fn $0new() -> Self { Self { } }
298 296
299 fn qux(&self) {} 297 fn qux(&self) {}
300 fn baz() -> i32 { 298 fn baz() -> i32 {
@@ -311,7 +309,7 @@ impl Foo {
311"pub struct Foo {} 309"pub struct Foo {}
312 310
313impl Foo { 311impl Foo {
314 pub fn new() -> Self { Self { } }<|> 312 pub fn $0new() -> Self { Self { } }
315} 313}
316", 314",
317 ); 315 );
@@ -321,7 +319,7 @@ impl Foo {
321"pub(crate) struct Foo {} 319"pub(crate) struct Foo {}
322 320
323impl Foo { 321impl Foo {
324 pub(crate) fn new() -> Self { Self { } }<|> 322 pub(crate) fn $0new() -> Self { Self { } }
325} 323}
326", 324",
327 ); 325 );
@@ -414,7 +412,7 @@ pub struct Source<T> {
414} 412}
415 413
416impl<T> Source<T> { 414impl<T> Source<T> {
417 pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|> 415 pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
418 416
419 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 417 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
420 Source { file_id: self.file_id, ast: f(self.ast) } 418 Source { file_id: self.file_id, ast: f(self.ast) }
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
new file mode 100644
index 000000000..26acf81f2
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -0,0 +1,134 @@
1use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
2use ra_syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4
5use crate::{
6 assist_context::{AssistContext, Assists},
7 AssistId,
8};
9
10// Assist: add_turbo_fish
11//
12// Adds `::<_>` to a call of a generic method or function.
13//
14// ```
15// fn make<T>() -> T { todo!() }
16// fn main() {
17// let x = make<|>();
18// }
19// ```
20// ->
21// ```
22// fn make<T>() -> T { todo!() }
23// fn main() {
24// let x = make::<${0:_}>();
25// }
26// ```
27pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?;
29 let next_token = ident.next_token()?;
30 if next_token.kind() == T![::] {
31 mark::hit!(add_turbo_fish_one_fish_is_enough);
32 return None;
33 }
34 let name_ref = ast::NameRef::cast(ident.parent())?;
35 let def = match classify_name_ref(&ctx.sema, &name_ref)? {
36 NameRefClass::Definition(def) => def,
37 NameRefClass::FieldShorthand { .. } => return None,
38 };
39 let fun = match def {
40 Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
41 _ => return None,
42 };
43 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
44 if generics.is_empty() {
45 mark::hit!(add_turbo_fish_non_generic);
46 return None;
47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
49 match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"),
52 }
53 })
54}
55
56#[cfg(test)]
57mod tests {
58 use crate::tests::{check_assist, check_assist_not_applicable};
59
60 use super::*;
61 use test_utils::mark;
62
63 #[test]
64 fn add_turbo_fish_function() {
65 check_assist(
66 add_turbo_fish,
67 r#"
68fn make<T>() -> T {}
69fn main() {
70 make<|>();
71}
72"#,
73 r#"
74fn make<T>() -> T {}
75fn main() {
76 make::<${0:_}>();
77}
78"#,
79 );
80 }
81
82 #[test]
83 fn add_turbo_fish_method() {
84 check_assist(
85 add_turbo_fish,
86 r#"
87struct S;
88impl S {
89 fn make<T>(&self) -> T {}
90}
91fn main() {
92 S.make<|>();
93}
94"#,
95 r#"
96struct S;
97impl S {
98 fn make<T>(&self) -> T {}
99}
100fn main() {
101 S.make::<${0:_}>();
102}
103"#,
104 );
105 }
106
107 #[test]
108 fn add_turbo_fish_one_fish_is_enough() {
109 mark::check!(add_turbo_fish_one_fish_is_enough);
110 check_assist_not_applicable(
111 add_turbo_fish,
112 r#"
113fn make<T>() -> T {}
114fn main() {
115 make<|>::<()>();
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn add_turbo_fish_non_generic() {
123 mark::check!(add_turbo_fish_non_generic);
124 check_assist_not_applicable(
125 add_turbo_fish,
126 r#"
127fn make() -> () {}
128fn main() {
129 make<|>();
130}
131"#,
132 );
133 }
134}
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 0feba5e11..233e8fb8e 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -63,22 +63,22 @@ mod tests {
63 63
64 #[test] 64 #[test]
65 fn demorgan_turns_and_into_or() { 65 fn demorgan_turns_and_into_or() {
66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") 66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }")
67 } 67 }
68 68
69 #[test] 69 #[test]
70 fn demorgan_turns_or_into_and() { 70 fn demorgan_turns_or_into_and() {
71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") 71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }")
72 } 72 }
73 73
74 #[test] 74 #[test]
75 fn demorgan_removes_inequality() { 75 fn demorgan_removes_inequality() {
76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") 76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }")
77 } 77 }
78 78
79 #[test] 79 #[test]
80 fn demorgan_general_case() { 80 fn demorgan_general_case() {
81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") 81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }")
82 } 82 }
83 83
84 #[test] 84 #[test]
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 78d23150d..edf96d50e 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -50,7 +50,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
50 format!("Import `{}`", &import), 50 format!("Import `{}`", &import),
51 range, 51 range,
52 |builder| { 52 |builder| {
53 insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); 53 insert_use_statement(
54 &auto_import_assets.syntax_under_caret,
55 &import,
56 ctx,
57 builder.text_edit_builder(),
58 );
54 }, 59 },
55 ); 60 );
56 } 61 }
@@ -293,7 +298,7 @@ mod tests {
293 } 298 }
294 ", 299 ",
295 r" 300 r"
296 <|>use PubMod::PubStruct; 301 use PubMod::PubStruct;
297 302
298 PubStruct 303 PubStruct
299 304
@@ -324,7 +329,7 @@ mod tests {
324 macro_rules! foo { 329 macro_rules! foo {
325 ($i:ident) => { fn foo(a: $i) {} } 330 ($i:ident) => { fn foo(a: $i) {} }
326 } 331 }
327 foo!(Pub<|>Struct); 332 foo!(PubStruct);
328 333
329 pub mod PubMod { 334 pub mod PubMod {
330 pub struct PubStruct; 335 pub struct PubStruct;
@@ -355,7 +360,7 @@ mod tests {
355 use PubMod::{PubStruct2, PubStruct1}; 360 use PubMod::{PubStruct2, PubStruct1};
356 361
357 struct Test { 362 struct Test {
358 test: Pub<|>Struct2<u8>, 363 test: PubStruct2<u8>,
359 } 364 }
360 365
361 pub mod PubMod { 366 pub mod PubMod {
@@ -388,7 +393,7 @@ mod tests {
388 r" 393 r"
389 use PubMod3::PubStruct; 394 use PubMod3::PubStruct;
390 395
391 PubSt<|>ruct 396 PubStruct
392 397
393 pub mod PubMod1 { 398 pub mod PubMod1 {
394 pub struct PubStruct; 399 pub struct PubStruct;
@@ -469,7 +474,7 @@ mod tests {
469 r" 474 r"
470 use PubMod::test_function; 475 use PubMod::test_function;
471 476
472 test_function<|> 477 test_function
473 478
474 pub mod PubMod { 479 pub mod PubMod {
475 pub fn test_function() {}; 480 pub fn test_function() {};
@@ -496,7 +501,7 @@ mod tests {
496 r"use crate_with_macro::foo; 501 r"use crate_with_macro::foo;
497 502
498fn main() { 503fn main() {
499 foo<|> 504 foo
500} 505}
501", 506",
502 ); 507 );
@@ -582,7 +587,7 @@ fn main() {
582 } 587 }
583 588
584 fn main() { 589 fn main() {
585 TestStruct::test_function<|> 590 TestStruct::test_function
586 } 591 }
587 ", 592 ",
588 ); 593 );
@@ -615,7 +620,7 @@ fn main() {
615 } 620 }
616 621
617 fn main() { 622 fn main() {
618 TestStruct::TEST_CONST<|> 623 TestStruct::TEST_CONST
619 } 624 }
620 ", 625 ",
621 ); 626 );
@@ -654,7 +659,7 @@ fn main() {
654 } 659 }
655 660
656 fn main() { 661 fn main() {
657 test_mod::TestStruct::test_function<|> 662 test_mod::TestStruct::test_function
658 } 663 }
659 ", 664 ",
660 ); 665 );
@@ -725,7 +730,7 @@ fn main() {
725 } 730 }
726 731
727 fn main() { 732 fn main() {
728 test_mod::TestStruct::TEST_CONST<|> 733 test_mod::TestStruct::TEST_CONST
729 } 734 }
730 ", 735 ",
731 ); 736 );
@@ -798,7 +803,7 @@ fn main() {
798 803
799 fn main() { 804 fn main() {
800 let test_struct = test_mod::TestStruct {}; 805 let test_struct = test_mod::TestStruct {};
801 test_struct.test_meth<|>od() 806 test_struct.test_method()
802 } 807 }
803 ", 808 ",
804 ); 809 );
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 5c907097e..c6baa0a57 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
@@ -1,8 +1,6 @@
1use ra_syntax::{ 1use ra_syntax::{
2 ast::{self, BlockExpr, Expr, LoopBodyOwner}, 2 ast::{self, BlockExpr, Expr, LoopBodyOwner},
3 AstNode, 3 AstNode, SyntaxNode,
4 SyntaxKind::{COMMENT, WHITESPACE},
5 SyntaxNode, TextSize,
6}; 4};
7 5
8use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, Assists};
@@ -16,39 +14,40 @@ use crate::{AssistContext, AssistId, Assists};
16// ``` 14// ```
17// -> 15// ->
18// ``` 16// ```
19// fn foo() -> Result<i32, > { Ok(42i32) } 17// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
20// ``` 18// ```
21pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 19pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
22 let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); 20 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
23 let fn_def = &mut fn_def?; 21 // FIXME: extend to lambdas as well
24 let ret_type = &fn_def.ret_type()?.type_ref()?; 22 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
25 if ret_type.syntax().text().to_string().starts_with("Result<") { 23
24 let type_ref = &ret_type.type_ref()?;
25 if type_ref.syntax().text().to_string().starts_with("Result<") {
26 return None; 26 return None;
27 } 27 }
28 28
29 let block_expr = &fn_def.body()?; 29 let block_expr = &fn_def.body()?;
30 let cursor_in_ret_type =
31 fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range);
32 if !cursor_in_ret_type {
33 return None;
34 }
35 30
36 acc.add( 31 acc.add(
37 AssistId("change_return_type_to_result"), 32 AssistId("change_return_type_to_result"),
38 "Change return type to Result", 33 "Change return type to Result",
39 ret_type.syntax().text_range(), 34 type_ref.syntax().text_range(),
40 |edit| { 35 |builder| {
41 let mut tail_return_expr_collector = TailReturnCollector::new(); 36 let mut tail_return_expr_collector = TailReturnCollector::new();
42 tail_return_expr_collector.collect_jump_exprs(block_expr, false); 37 tail_return_expr_collector.collect_jump_exprs(block_expr, false);
43 tail_return_expr_collector.collect_tail_exprs(block_expr); 38 tail_return_expr_collector.collect_tail_exprs(block_expr);
44 39
45 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { 40 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
46 edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); 41 builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
47 } 42 }
48 edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type));
49 43
50 if let Some(node_start) = result_insertion_offset(&ret_type) { 44 match ctx.config.snippet_cap {
51 edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type))); 45 Some(cap) => {
46 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
47 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
48 }
49 None => builder
50 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
52 } 51 }
53 }, 52 },
54 ) 53 )
@@ -250,17 +249,8 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
250 } 249 }
251} 250}
252 251
253fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> {
254 let non_ws_child = ret_type
255 .syntax()
256 .children_with_tokens()
257 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
258 Some(non_ws_child.text_range().start())
259}
260
261#[cfg(test)] 252#[cfg(test)]
262mod tests { 253mod tests {
263
264 use crate::tests::{check_assist, check_assist_not_applicable}; 254 use crate::tests::{check_assist, check_assist_not_applicable};
265 255
266 use super::*; 256 use super::*;
@@ -273,7 +263,7 @@ mod tests {
273 let test = "test"; 263 let test = "test";
274 return 42i32; 264 return 42i32;
275 }"#, 265 }"#,
276 r#"fn foo() -> Result<i32, <|>> { 266 r#"fn foo() -> Result<i32, ${0:_}> {
277 let test = "test"; 267 let test = "test";
278 return Ok(42i32); 268 return Ok(42i32);
279 }"#, 269 }"#,
@@ -288,7 +278,7 @@ mod tests {
288 let test = "test"; 278 let test = "test";
289 return 42i32; 279 return 42i32;
290 }"#, 280 }"#,
291 r#"fn foo() -> Result<i32, <|>> { 281 r#"fn foo() -> Result<i32, ${0:_}> {
292 let test = "test"; 282 let test = "test";
293 return Ok(42i32); 283 return Ok(42i32);
294 }"#, 284 }"#,
@@ -314,7 +304,7 @@ mod tests {
314 let test = "test"; 304 let test = "test";
315 return 42i32; 305 return 42i32;
316 }"#, 306 }"#,
317 r#"fn foo() -> Result<i32, <|>> { 307 r#"fn foo() -> Result<i32, ${0:_}> {
318 let test = "test"; 308 let test = "test";
319 return Ok(42i32); 309 return Ok(42i32);
320 }"#, 310 }"#,
@@ -329,7 +319,7 @@ mod tests {
329 let test = "test"; 319 let test = "test";
330 42i32 320 42i32
331 }"#, 321 }"#,
332 r#"fn foo() -> Result<i32, <|>> { 322 r#"fn foo() -> Result<i32, ${0:_}> {
333 let test = "test"; 323 let test = "test";
334 Ok(42i32) 324 Ok(42i32)
335 }"#, 325 }"#,
@@ -343,7 +333,7 @@ mod tests {
343 r#"fn foo() -> i32<|> { 333 r#"fn foo() -> i32<|> {
344 42i32 334 42i32
345 }"#, 335 }"#,
346 r#"fn foo() -> Result<i32, <|>> { 336 r#"fn foo() -> Result<i32, ${0:_}> {
347 Ok(42i32) 337 Ok(42i32)
348 }"#, 338 }"#,
349 ); 339 );
@@ -359,7 +349,7 @@ mod tests {
359 24i32 349 24i32
360 } 350 }
361 }"#, 351 }"#,
362 r#"fn foo() -> Result<i32, <|>> { 352 r#"fn foo() -> Result<i32, ${0:_}> {
363 if true { 353 if true {
364 Ok(42i32) 354 Ok(42i32)
365 } else { 355 } else {
@@ -384,7 +374,7 @@ mod tests {
384 24i32 374 24i32
385 } 375 }
386 }"#, 376 }"#,
387 r#"fn foo() -> Result<i32, <|>> { 377 r#"fn foo() -> Result<i32, ${0:_}> {
388 if true { 378 if true {
389 if false { 379 if false {
390 Ok(1) 380 Ok(1)
@@ -413,7 +403,7 @@ mod tests {
413 24i32.await 403 24i32.await
414 } 404 }
415 }"#, 405 }"#,
416 r#"async fn foo() -> Result<i32, <|>> { 406 r#"async fn foo() -> Result<i32, ${0:_}> {
417 if true { 407 if true {
418 if false { 408 if false {
419 Ok(1.await) 409 Ok(1.await)
@@ -434,7 +424,7 @@ mod tests {
434 r#"fn foo() -> [i32;<|> 3] { 424 r#"fn foo() -> [i32;<|> 3] {
435 [1, 2, 3] 425 [1, 2, 3]
436 }"#, 426 }"#,
437 r#"fn foo() -> Result<[i32; 3], <|>> { 427 r#"fn foo() -> Result<[i32; 3], ${0:_}> {
438 Ok([1, 2, 3]) 428 Ok([1, 2, 3])
439 }"#, 429 }"#,
440 ); 430 );
@@ -455,7 +445,7 @@ mod tests {
455 24 as i32 445 24 as i32
456 } 446 }
457 }"#, 447 }"#,
458 r#"fn foo() -> Result<i32, <|>> { 448 r#"fn foo() -> Result<i32, ${0:_}> {
459 if true { 449 if true {
460 if false { 450 if false {
461 Ok(1 as i32) 451 Ok(1 as i32)
@@ -480,7 +470,7 @@ mod tests {
480 _ => 24i32, 470 _ => 24i32,
481 } 471 }
482 }"#, 472 }"#,
483 r#"fn foo() -> Result<i32, <|>> { 473 r#"fn foo() -> Result<i32, ${0:_}> {
484 let my_var = 5; 474 let my_var = 5;
485 match my_var { 475 match my_var {
486 5 => Ok(42i32), 476 5 => Ok(42i32),
@@ -503,7 +493,7 @@ mod tests {
503 493
504 my_var 494 my_var
505 }"#, 495 }"#,
506 r#"fn foo() -> Result<i32, <|>> { 496 r#"fn foo() -> Result<i32, ${0:_}> {
507 let my_var = 5; 497 let my_var = 5;
508 loop { 498 loop {
509 println!("test"); 499 println!("test");
@@ -526,7 +516,7 @@ mod tests {
526 516
527 my_var 517 my_var
528 }"#, 518 }"#,
529 r#"fn foo() -> Result<i32, <|>> { 519 r#"fn foo() -> Result<i32, ${0:_}> {
530 let my_var = let x = loop { 520 let my_var = let x = loop {
531 break 1; 521 break 1;
532 }; 522 };
@@ -549,7 +539,7 @@ mod tests {
549 539
550 res 540 res
551 }"#, 541 }"#,
552 r#"fn foo() -> Result<i32, <|>> { 542 r#"fn foo() -> Result<i32, ${0:_}> {
553 let my_var = 5; 543 let my_var = 5;
554 let res = match my_var { 544 let res = match my_var {
555 5 => 42i32, 545 5 => 42i32,
@@ -572,7 +562,7 @@ mod tests {
572 562
573 res 563 res
574 }"#, 564 }"#,
575 r#"fn foo() -> Result<i32, <|>> { 565 r#"fn foo() -> Result<i32, ${0:_}> {
576 let my_var = 5; 566 let my_var = 5;
577 let res = if my_var == 5 { 567 let res = if my_var == 5 {
578 42i32 568 42i32
@@ -608,7 +598,7 @@ mod tests {
608 }, 598 },
609 } 599 }
610 }"#, 600 }"#,
611 r#"fn foo() -> Result<i32, <|>> { 601 r#"fn foo() -> Result<i32, ${0:_}> {
612 let my_var = 5; 602 let my_var = 5;
613 match my_var { 603 match my_var {
614 5 => { 604 5 => {
@@ -641,7 +631,7 @@ mod tests {
641 } 631 }
642 53i32 632 53i32
643 }"#, 633 }"#,
644 r#"fn foo() -> Result<i32, <|>> { 634 r#"fn foo() -> Result<i32, ${0:_}> {
645 let test = "test"; 635 let test = "test";
646 if test == "test" { 636 if test == "test" {
647 return Ok(24i32); 637 return Ok(24i32);
@@ -672,7 +662,7 @@ mod tests {
672 662
673 the_field 663 the_field
674 }"#, 664 }"#,
675 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 665 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
676 let true_closure = || { 666 let true_closure = || {
677 return true; 667 return true;
678 }; 668 };
@@ -711,7 +701,7 @@ mod tests {
711 701
712 t.unwrap_or_else(|| the_field) 702 t.unwrap_or_else(|| the_field)
713 }"#, 703 }"#,
714 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 704 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
715 let true_closure = || { 705 let true_closure = || {
716 return true; 706 return true;
717 }; 707 };
@@ -749,7 +739,7 @@ mod tests {
749 i += 1; 739 i += 1;
750 } 740 }
751 }"#, 741 }"#,
752 r#"fn foo() -> Result<i32, <|>> { 742 r#"fn foo() -> Result<i32, ${0:_}> {
753 let test = "test"; 743 let test = "test";
754 if test == "test" { 744 if test == "test" {
755 return Ok(24i32); 745 return Ok(24i32);
@@ -781,7 +771,7 @@ mod tests {
781 } 771 }
782 } 772 }
783 }"#, 773 }"#,
784 r#"fn foo() -> Result<i32, <|>> { 774 r#"fn foo() -> Result<i32, ${0:_}> {
785 let test = "test"; 775 let test = "test";
786 if test == "test" { 776 if test == "test" {
787 return Ok(24i32); 777 return Ok(24i32);
@@ -819,7 +809,7 @@ mod tests {
819 } 809 }
820 } 810 }
821 }"#, 811 }"#,
822 r#"fn foo() -> Result<i32, <|>> { 812 r#"fn foo() -> Result<i32, ${0:_}> {
823 let test = "test"; 813 let test = "test";
824 let other = 5; 814 let other = 5;
825 if test == "test" { 815 if test == "test" {
@@ -860,7 +850,7 @@ mod tests {
860 850
861 the_field 851 the_field
862 }"#, 852 }"#,
863 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 853 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
864 if the_field < 5 { 854 if the_field < 5 {
865 let mut i = 0; 855 let mut i = 0;
866 loop { 856 loop {
@@ -894,7 +884,7 @@ mod tests {
894 884
895 the_field 885 the_field
896 }"#, 886 }"#,
897 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 887 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
898 if the_field < 5 { 888 if the_field < 5 {
899 let mut i = 0; 889 let mut i = 0;
900 890
@@ -923,7 +913,7 @@ mod tests {
923 913
924 the_field 914 the_field
925 }"#, 915 }"#,
926 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 916 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
927 if the_field < 5 { 917 if the_field < 5 {
928 let mut i = 0; 918 let mut i = 0;
929 919
@@ -953,7 +943,7 @@ mod tests {
953 943
954 the_field 944 the_field
955 }"#, 945 }"#,
956 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 946 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
957 if the_field < 5 { 947 if the_field < 5 {
958 let mut i = 0; 948 let mut i = 0;
959 949
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 40cf4b422..c21d75be0 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,14 +5,11 @@ use ra_syntax::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextRange, TextSize, T, 8 SyntaxNode, TextSize, T,
9}; 9};
10 10use test_utils::mark;
11use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
12use test_utils::tested_by;
13 11
14use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, Assists};
15use ra_db::FileId;
16 13
17// Assist: change_visibility 14// Assist: change_visibility
18// 15//
@@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
30 return change_vis(acc, vis); 27 return change_vis(acc, vis);
31 } 28 }
32 add_vis(acc, ctx) 29 add_vis(acc, ctx)
33 .or_else(|| add_vis_to_referenced_module_def(acc, ctx))
34 .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
35} 30}
36 31
37fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -55,7 +50,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
55 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { 50 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
56 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; 51 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?;
57 if field.name()? != field_name { 52 if field.name()? != field_name {
58 tested_by!(change_visibility_field_false_positive); 53 mark::hit!(change_visibility_field_false_positive);
59 return None; 54 return None;
60 } 55 }
61 if field.visibility().is_some() { 56 if field.visibility().is_some() {
@@ -73,147 +68,9 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
73 68
74 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { 69 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| {
75 edit.insert(offset, "pub(crate) "); 70 edit.insert(offset, "pub(crate) ");
76 edit.set_cursor(offset);
77 })
78}
79
80fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let path: ast::Path = ctx.find_node_at_offset()?;
82 let path_res = ctx.sema.resolve_path(&path)?;
83 let def = match path_res {
84 PathResolution::Def(def) => def,
85 _ => return None,
86 };
87
88 let current_module = ctx.sema.scope(&path.syntax()).module()?;
89 let target_module = def.module(ctx.db)?;
90
91 let vis = target_module.visibility_of(ctx.db, &def)?;
92 if vis.is_visible_from(ctx.db, current_module.into()) {
93 return None;
94 };
95
96 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
97
98 let missing_visibility =
99 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
100
101 let assist_label = match target_name {
102 None => format!("Change visibility to {}", missing_visibility),
103 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
104 };
105
106 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
107 edit.set_file(target_file);
108 edit.insert(offset, format!("{} ", missing_visibility));
109 edit.set_cursor(offset);
110 })
111}
112
113fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
114 let record_field: ast::RecordField = ctx.find_node_at_offset()?;
115 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
116
117 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
118 let visibility = record_field_def.visibility(ctx.db);
119 if visibility.is_visible_from(ctx.db, current_module.into()) {
120 return None;
121 }
122
123 let parent = record_field_def.parent_def(ctx.db);
124 let parent_name = parent.name(ctx.db);
125 let target_module = parent.module(ctx.db);
126
127 let in_file_source = record_field_def.source(ctx.db);
128 let (offset, target) = match in_file_source.value {
129 hir::FieldSource::Named(it) => {
130 let s = it.syntax();
131 (vis_offset(s), s.text_range())
132 }
133 hir::FieldSource::Pos(it) => {
134 let s = it.syntax();
135 (vis_offset(s), s.text_range())
136 }
137 };
138
139 let missing_visibility =
140 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
141 let target_file = in_file_source.file_id.original_file(ctx.db);
142
143 let target_name = record_field_def.name(ctx.db);
144 let assist_label =
145 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
146
147 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
148 edit.set_file(target_file);
149 edit.insert(offset, format!("{} ", missing_visibility));
150 edit.set_cursor(offset)
151 }) 71 })
152} 72}
153 73
154fn target_data_for_def(
155 db: &dyn HirDatabase,
156 def: hir::ModuleDef,
157) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
158 fn offset_target_and_file_id<S, Ast>(
159 db: &dyn HirDatabase,
160 x: S,
161 ) -> (TextSize, TextRange, FileId)
162 where
163 S: HasSource<Ast = Ast>,
164 Ast: AstNode,
165 {
166 let source = x.source(db);
167 let in_file_syntax = source.syntax();
168 let file_id = in_file_syntax.file_id;
169 let syntax = in_file_syntax.value;
170 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
171 }
172
173 let target_name;
174 let (offset, target, target_file) = match def {
175 hir::ModuleDef::Function(f) => {
176 target_name = Some(f.name(db));
177 offset_target_and_file_id(db, f)
178 }
179 hir::ModuleDef::Adt(adt) => {
180 target_name = Some(adt.name(db));
181 match adt {
182 hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
183 hir::Adt::Union(u) => offset_target_and_file_id(db, u),
184 hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
185 }
186 }
187 hir::ModuleDef::Const(c) => {
188 target_name = c.name(db);
189 offset_target_and_file_id(db, c)
190 }
191 hir::ModuleDef::Static(s) => {
192 target_name = s.name(db);
193 offset_target_and_file_id(db, s)
194 }
195 hir::ModuleDef::Trait(t) => {
196 target_name = Some(t.name(db));
197 offset_target_and_file_id(db, t)
198 }
199 hir::ModuleDef::TypeAlias(t) => {
200 target_name = Some(t.name(db));
201 offset_target_and_file_id(db, t)
202 }
203 hir::ModuleDef::Module(m) => {
204 target_name = m.name(db);
205 let in_file_source = m.declaration_source(db)?;
206 let file_id = in_file_source.file_id.original_file(db.upcast());
207 let syntax = in_file_source.value.syntax();
208 (vis_offset(syntax), syntax.text_range(), file_id)
209 }
210 // Enum variants can't be private, we can't modify builtin types
211 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
212 };
213
214 Some((offset, target, target_file, target_name))
215}
216
217fn vis_offset(node: &SyntaxNode) -> TextSize { 74fn vis_offset(node: &SyntaxNode) -> TextSize {
218 node.children_with_tokens() 75 node.children_with_tokens()
219 .skip_while(|it| match it.kind() { 76 .skip_while(|it| match it.kind() {
@@ -234,7 +91,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
234 target, 91 target,
235 |edit| { 92 |edit| {
236 edit.replace(vis.syntax().text_range(), "pub(crate)"); 93 edit.replace(vis.syntax().text_range(), "pub(crate)");
237 edit.set_cursor(vis.syntax().text_range().start())
238 }, 94 },
239 ); 95 );
240 } 96 }
@@ -246,7 +102,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
246 target, 102 target,
247 |edit| { 103 |edit| {
248 edit.replace(vis.syntax().text_range(), "pub"); 104 edit.replace(vis.syntax().text_range(), "pub");
249 edit.set_cursor(vis.syntax().text_range().start());
250 }, 105 },
251 ); 106 );
252 } 107 }
@@ -255,7 +110,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
255 110
256#[cfg(test)] 111#[cfg(test)]
257mod tests { 112mod tests {
258 use test_utils::covers; 113 use test_utils::mark;
259 114
260 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
261 116
@@ -263,17 +118,13 @@ mod tests {
263 118
264 #[test] 119 #[test]
265 fn change_visibility_adds_pub_crate_to_items() { 120 fn change_visibility_adds_pub_crate_to_items() {
266 check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); 121 check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}");
267 check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}"); 122 check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}");
268 check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); 123 check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}");
269 check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); 124 check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}");
270 check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); 125 check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}");
271 check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}"); 126 check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}");
272 check_assist( 127 check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}");
273 change_visibility,
274 "unsafe f<|>n foo() {}",
275 "<|>pub(crate) unsafe fn foo() {}",
276 );
277 } 128 }
278 129
279 #[test] 130 #[test]
@@ -281,14 +132,14 @@ mod tests {
281 check_assist( 132 check_assist(
282 change_visibility, 133 change_visibility,
283 r"struct S { <|>field: u32 }", 134 r"struct S { <|>field: u32 }",
284 r"struct S { <|>pub(crate) field: u32 }", 135 r"struct S { pub(crate) field: u32 }",
285 ); 136 );
286 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); 137 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )");
287 } 138 }
288 139
289 #[test] 140 #[test]
290 fn change_visibility_field_false_positive() { 141 fn change_visibility_field_false_positive() {
291 covers!(change_visibility_field_false_positive); 142 mark::check!(change_visibility_field_false_positive);
292 check_assist_not_applicable( 143 check_assist_not_applicable(
293 change_visibility, 144 change_visibility,
294 r"struct S { field: [(); { let <|>x = ();}] }", 145 r"struct S { field: [(); { let <|>x = ();}] }",
@@ -297,17 +148,17 @@ mod tests {
297 148
298 #[test] 149 #[test]
299 fn change_visibility_pub_to_pub_crate() { 150 fn change_visibility_pub_to_pub_crate() {
300 check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}") 151 check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}")
301 } 152 }
302 153
303 #[test] 154 #[test]
304 fn change_visibility_pub_crate_to_pub() { 155 fn change_visibility_pub_crate_to_pub() {
305 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}") 156 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}")
306 } 157 }
307 158
308 #[test] 159 #[test]
309 fn change_visibility_const() { 160 fn change_visibility_const() {
310 check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;"); 161 check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
311 } 162 }
312 163
313 #[test] 164 #[test]
@@ -328,199 +179,12 @@ mod tests {
328 // comments 179 // comments
329 180
330 #[derive(Debug)] 181 #[derive(Debug)]
331 <|>pub(crate) struct Foo; 182 pub(crate) struct Foo;
332 ", 183 ",
333 ) 184 )
334 } 185 }
335 186
336 #[test] 187 #[test]
337 fn change_visibility_of_fn_via_path() {
338 check_assist(
339 change_visibility,
340 r"mod foo { fn foo() {} }
341 fn main() { foo::foo<|>() } ",
342 r"mod foo { <|>pub(crate) fn foo() {} }
343 fn main() { foo::foo() } ",
344 );
345 check_assist_not_applicable(
346 change_visibility,
347 r"mod foo { pub fn foo() {} }
348 fn main() { foo::foo<|>() } ",
349 )
350 }
351
352 #[test]
353 fn change_visibility_of_adt_in_submodule_via_path() {
354 check_assist(
355 change_visibility,
356 r"mod foo { struct Foo; }
357 fn main() { foo::Foo<|> } ",
358 r"mod foo { <|>pub(crate) struct Foo; }
359 fn main() { foo::Foo } ",
360 );
361 check_assist_not_applicable(
362 change_visibility,
363 r"mod foo { pub struct Foo; }
364 fn main() { foo::Foo<|> } ",
365 );
366 check_assist(
367 change_visibility,
368 r"mod foo { enum Foo; }
369 fn main() { foo::Foo<|> } ",
370 r"mod foo { <|>pub(crate) enum Foo; }
371 fn main() { foo::Foo } ",
372 );
373 check_assist_not_applicable(
374 change_visibility,
375 r"mod foo { pub enum Foo; }
376 fn main() { foo::Foo<|> } ",
377 );
378 check_assist(
379 change_visibility,
380 r"mod foo { union Foo; }
381 fn main() { foo::Foo<|> } ",
382 r"mod foo { <|>pub(crate) union Foo; }
383 fn main() { foo::Foo } ",
384 );
385 check_assist_not_applicable(
386 change_visibility,
387 r"mod foo { pub union Foo; }
388 fn main() { foo::Foo<|> } ",
389 );
390 }
391
392 #[test]
393 fn change_visibility_of_adt_in_other_file_via_path() {
394 check_assist(
395 change_visibility,
396 r"
397 //- /main.rs
398 mod foo;
399 fn main() { foo::Foo<|> }
400
401 //- /foo.rs
402 struct Foo;
403 ",
404 r"<|>pub(crate) struct Foo;
405
406",
407 );
408 }
409
410 #[test]
411 fn change_visibility_of_struct_field_via_path() {
412 check_assist(
413 change_visibility,
414 r"mod foo { pub struct Foo { bar: (), } }
415 fn main() { foo::Foo { <|>bar: () }; } ",
416 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
417 fn main() { foo::Foo { bar: () }; } ",
418 );
419 check_assist(
420 change_visibility,
421 r"//- /lib.rs
422 mod foo;
423 fn main() { foo::Foo { <|>bar: () }; }
424 //- /foo.rs
425 pub struct Foo { bar: () }
426 ",
427 r"pub struct Foo { <|>pub(crate) bar: () }
428
429",
430 );
431 check_assist_not_applicable(
432 change_visibility,
433 r"mod foo { pub struct Foo { pub bar: (), } }
434 fn main() { foo::Foo { <|>bar: () }; } ",
435 );
436 check_assist_not_applicable(
437 change_visibility,
438 r"//- /lib.rs
439 mod foo;
440 fn main() { foo::Foo { <|>bar: () }; }
441 //- /foo.rs
442 pub struct Foo { pub bar: () }
443 ",
444 );
445 }
446
447 #[test]
448 fn change_visibility_of_enum_variant_field_via_path() {
449 check_assist(
450 change_visibility,
451 r"mod foo { pub enum Foo { Bar { bar: () } } }
452 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
453 r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
454 fn main() { foo::Foo::Bar { bar: () }; } ",
455 );
456 check_assist(
457 change_visibility,
458 r"//- /lib.rs
459 mod foo;
460 fn main() { foo::Foo::Bar { <|>bar: () }; }
461 //- /foo.rs
462 pub enum Foo { Bar { bar: () } }
463 ",
464 r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
465
466",
467 );
468 check_assist_not_applicable(
469 change_visibility,
470 r"mod foo { pub struct Foo { pub bar: (), } }
471 fn main() { foo::Foo { <|>bar: () }; } ",
472 );
473 check_assist_not_applicable(
474 change_visibility,
475 r"//- /lib.rs
476 mod foo;
477 fn main() { foo::Foo { <|>bar: () }; }
478 //- /foo.rs
479 pub struct Foo { pub bar: () }
480 ",
481 );
482 }
483
484 #[test]
485 #[ignore]
486 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
487 fn change_visibility_of_union_field_via_path() {
488 check_assist(
489 change_visibility,
490 r"mod foo { pub union Foo { bar: (), } }
491 fn main() { foo::Foo { <|>bar: () }; } ",
492 r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
493 fn main() { foo::Foo { bar: () }; } ",
494 );
495 check_assist(
496 change_visibility,
497 r"//- /lib.rs
498 mod foo;
499 fn main() { foo::Foo { <|>bar: () }; }
500 //- /foo.rs
501 pub union Foo { bar: () }
502 ",
503 r"pub union Foo { <|>pub(crate) bar: () }
504
505",
506 );
507 check_assist_not_applicable(
508 change_visibility,
509 r"mod foo { pub union Foo { pub bar: (), } }
510 fn main() { foo::Foo { <|>bar: () }; } ",
511 );
512 check_assist_not_applicable(
513 change_visibility,
514 r"//- /lib.rs
515 mod foo;
516 fn main() { foo::Foo { <|>bar: () }; }
517 //- /foo.rs
518 pub union Foo { pub bar: () }
519 ",
520 );
521 }
522
523 #[test]
524 fn not_applicable_for_enum_variants() { 188 fn not_applicable_for_enum_variants() {
525 check_assist_not_applicable( 189 check_assist_not_applicable(
526 change_visibility, 190 change_visibility,
@@ -530,182 +194,6 @@ mod tests {
530 } 194 }
531 195
532 #[test] 196 #[test]
533 fn change_visibility_of_const_via_path() {
534 check_assist(
535 change_visibility,
536 r"mod foo { const FOO: () = (); }
537 fn main() { foo::FOO<|> } ",
538 r"mod foo { <|>pub(crate) const FOO: () = (); }
539 fn main() { foo::FOO } ",
540 );
541 check_assist_not_applicable(
542 change_visibility,
543 r"mod foo { pub const FOO: () = (); }
544 fn main() { foo::FOO<|> } ",
545 );
546 }
547
548 #[test]
549 fn change_visibility_of_static_via_path() {
550 check_assist(
551 change_visibility,
552 r"mod foo { static FOO: () = (); }
553 fn main() { foo::FOO<|> } ",
554 r"mod foo { <|>pub(crate) static FOO: () = (); }
555 fn main() { foo::FOO } ",
556 );
557 check_assist_not_applicable(
558 change_visibility,
559 r"mod foo { pub static FOO: () = (); }
560 fn main() { foo::FOO<|> } ",
561 );
562 }
563
564 #[test]
565 fn change_visibility_of_trait_via_path() {
566 check_assist(
567 change_visibility,
568 r"mod foo { trait Foo { fn foo(&self) {} } }
569 fn main() { let x: &dyn foo::<|>Foo; } ",
570 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
571 fn main() { let x: &dyn foo::Foo; } ",
572 );
573 check_assist_not_applicable(
574 change_visibility,
575 r"mod foo { pub trait Foo { fn foo(&self) {} } }
576 fn main() { let x: &dyn foo::Foo<|>; } ",
577 );
578 }
579
580 #[test]
581 fn change_visibility_of_type_alias_via_path() {
582 check_assist(
583 change_visibility,
584 r"mod foo { type Foo = (); }
585 fn main() { let x: foo::Foo<|>; } ",
586 r"mod foo { <|>pub(crate) type Foo = (); }
587 fn main() { let x: foo::Foo; } ",
588 );
589 check_assist_not_applicable(
590 change_visibility,
591 r"mod foo { pub type Foo = (); }
592 fn main() { let x: foo::Foo<|>; } ",
593 );
594 }
595
596 #[test]
597 fn change_visibility_of_module_via_path() {
598 check_assist(
599 change_visibility,
600 r"mod foo { mod bar { fn bar() {} } }
601 fn main() { foo::bar<|>::bar(); } ",
602 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
603 fn main() { foo::bar::bar(); } ",
604 );
605
606 check_assist(
607 change_visibility,
608 r"
609 //- /main.rs
610 mod foo;
611 fn main() { foo::bar<|>::baz(); }
612
613 //- /foo.rs
614 mod bar {
615 pub fn baz() {}
616 }
617 ",
618 r"<|>pub(crate) mod bar {
619 pub fn baz() {}
620}
621
622",
623 );
624
625 check_assist_not_applicable(
626 change_visibility,
627 r"mod foo { pub mod bar { pub fn bar() {} } }
628 fn main() { foo::bar<|>::bar(); } ",
629 );
630 }
631
632 #[test]
633 fn change_visibility_of_inline_module_in_other_file_via_path() {
634 check_assist(
635 change_visibility,
636 r"
637 //- /main.rs
638 mod foo;
639 fn main() { foo::bar<|>::baz(); }
640
641 //- /foo.rs
642 mod bar;
643
644 //- /foo/bar.rs
645 pub fn baz() {}
646 }
647 ",
648 r"<|>pub(crate) mod bar;
649",
650 );
651 }
652
653 #[test]
654 fn change_visibility_of_module_declaration_in_other_file_via_path() {
655 check_assist(
656 change_visibility,
657 r"//- /main.rs
658 mod foo;
659 fn main() { foo::bar<|>>::baz(); }
660
661 //- /foo.rs
662 mod bar {
663 pub fn baz() {}
664 }",
665 r"<|>pub(crate) mod bar {
666 pub fn baz() {}
667}
668",
669 );
670 }
671
672 #[test]
673 #[ignore]
674 // FIXME handle reexports properly
675 fn change_visibility_of_reexport() {
676 check_assist(
677 change_visibility,
678 r"
679 mod foo {
680 use bar::Baz;
681 mod bar { pub(super) struct Baz; }
682 }
683 foo::Baz<|>
684 ",
685 r"
686 mod foo {
687 <|>pub(crate) use bar::Baz;
688 mod bar { pub(super) struct Baz; }
689 }
690 foo::Baz
691 ",
692 )
693 }
694
695 #[test]
696 fn adds_pub_when_target_is_in_another_crate() {
697 check_assist(
698 change_visibility,
699 r"//- /main.rs crate:a deps:foo
700 foo::Bar<|>
701 //- /lib.rs crate:foo
702 struct Bar;",
703 r"<|>pub struct Bar;
704",
705 )
706 }
707
708 #[test]
709 fn change_visibility_target() { 197 fn change_visibility_target() {
710 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 198 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
711 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 199 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 66b296081..4cc75a7ce 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
97 } 97 }
98 98
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 let cursor_position = ctx.offset();
101 100
102 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
103 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
@@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
148 } 147 }
149 }; 148 };
150 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
151 edit.set_cursor(cursor_position);
152 150
153 fn replace( 151 fn replace(
154 new_expr: &SyntaxNode, 152 new_expr: &SyntaxNode,
@@ -207,7 +205,7 @@ mod tests {
207 r#" 205 r#"
208 fn main() { 206 fn main() {
209 bar(); 207 bar();
210 if<|> !true { 208 if !true {
211 return; 209 return;
212 } 210 }
213 foo(); 211 foo();
@@ -237,7 +235,7 @@ mod tests {
237 r#" 235 r#"
238 fn main(n: Option<String>) { 236 fn main(n: Option<String>) {
239 bar(); 237 bar();
240 le<|>t n = match n { 238 let n = match n {
241 Some(it) => it, 239 Some(it) => it,
242 _ => return, 240 _ => return,
243 }; 241 };
@@ -263,7 +261,7 @@ mod tests {
263 "#, 261 "#,
264 r#" 262 r#"
265 fn main() { 263 fn main() {
266 le<|>t x = match Err(92) { 264 let x = match Err(92) {
267 Ok(it) => it, 265 Ok(it) => it,
268 _ => return, 266 _ => return,
269 }; 267 };
@@ -291,7 +289,7 @@ mod tests {
291 r#" 289 r#"
292 fn main(n: Option<String>) { 290 fn main(n: Option<String>) {
293 bar(); 291 bar();
294 le<|>t n = match n { 292 let n = match n {
295 Ok(it) => it, 293 Ok(it) => it,
296 _ => return, 294 _ => return,
297 }; 295 };
@@ -321,7 +319,7 @@ mod tests {
321 r#" 319 r#"
322 fn main() { 320 fn main() {
323 while true { 321 while true {
324 if<|> !true { 322 if !true {
325 continue; 323 continue;
326 } 324 }
327 foo(); 325 foo();
@@ -349,7 +347,7 @@ mod tests {
349 r#" 347 r#"
350 fn main() { 348 fn main() {
351 while true { 349 while true {
352 le<|>t n = match n { 350 let n = match n {
353 Some(it) => it, 351 Some(it) => it,
354 _ => continue, 352 _ => continue,
355 }; 353 };
@@ -378,7 +376,7 @@ mod tests {
378 r#" 376 r#"
379 fn main() { 377 fn main() {
380 loop { 378 loop {
381 if<|> !true { 379 if !true {
382 continue; 380 continue;
383 } 381 }
384 foo(); 382 foo();
@@ -406,7 +404,7 @@ mod tests {
406 r#" 404 r#"
407 fn main() { 405 fn main() {
408 loop { 406 loop {
409 le<|>t n = match n { 407 let n = match n {
410 Some(it) => it, 408 Some(it) => it,
411 _ => continue, 409 _ => continue,
412 }; 410 };
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 13c1e7e80..cc303285b 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -4,8 +4,12 @@ use hir::{Adt, HasSource, ModuleDef, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
7use test_utils::mark;
7 8
8use crate::{AssistContext, AssistId, Assists}; 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, Assists,
12};
9 13
10// Assist: fill_match_arms 14// Assist: fill_match_arms
11// 15//
@@ -26,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
26// 30//
27// fn handle(action: Action) { 31// fn handle(action: Action) {
28// match action { 32// match action {
29// Action::Move { distance } => {} 33// $0Action::Move { distance } => {}
30// Action::Stop => {} 34// Action::Stop => {}
31// } 35// }
32// } 36// }
@@ -49,12 +53,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
49 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) {
50 let variants = enum_def.variants(ctx.db); 54 let variants = enum_def.variants(ctx.db);
51 55
52 variants 56 let mut variants = variants
53 .into_iter() 57 .into_iter()
54 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db, module, variant))
55 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
56 .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()))
57 .collect() 61 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() {
63 // Match `Some` variant first.
64 mark::hit!(option_order);
65 variants.reverse()
66 }
67 variants
58 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
59 // Partial fill not currently supported for tuple of enums. 69 // Partial fill not currently supported for tuple of enums.
60 if !arms.is_empty() { 70 if !arms.is_empty() {
@@ -93,10 +103,23 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
93 } 103 }
94 104
95 let target = match_expr.syntax().text_range(); 105 let target = match_expr.syntax().text_range();
96 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { 106 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| {
97 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); 107 let new_arm_list = match_arm_list.remove_placeholder();
98 edit.set_cursor(expr.syntax().text_range().start()); 108 let n_old_arms = new_arm_list.arms().count();
99 edit.replace_ast(match_arm_list, new_arm_list); 109 let new_arm_list = new_arm_list.append_arms(missing_arms);
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
111 let old_range = match_arm_list.syntax().text_range();
112 match (first_new_arm, ctx.config.snippet_cap) {
113 (Some(first_new_arm), Some(cap)) => {
114 let snippet = render_snippet(
115 cap,
116 new_arm_list.syntax(),
117 Cursor::Before(first_new_arm.syntax()),
118 );
119 builder.replace_snippet(cap, old_range, snippet);
120 }
121 _ => builder.replace(old_range, new_arm_list.to_string()),
122 }
100 }) 123 })
101} 124}
102 125
@@ -167,7 +190,12 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
167 190
168#[cfg(test)] 191#[cfg(test)]
169mod tests { 192mod tests {
170 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 193 use test_utils::mark;
194
195 use crate::{
196 tests::{check_assist, check_assist_not_applicable, check_assist_target},
197 utils::FamousDefs,
198 };
171 199
172 use super::fill_match_arms; 200 use super::fill_match_arms;
173 201
@@ -214,12 +242,12 @@ mod tests {
214 r#" 242 r#"
215 enum A { 243 enum A {
216 As, 244 As,
217 Bs{x:i32, y:Option<i32>}, 245 Bs { x: i32, y: Option<i32> },
218 Cs(i32, Option<i32>), 246 Cs(i32, Option<i32>),
219 } 247 }
220 fn main() { 248 fn main() {
221 match A::As<|> { 249 match A::As<|> {
222 A::Bs{x,y:Some(_)} => {} 250 A::Bs { x, y: Some(_) } => {}
223 A::Cs(_, Some(_)) => {} 251 A::Cs(_, Some(_)) => {}
224 } 252 }
225 } 253 }
@@ -227,14 +255,14 @@ mod tests {
227 r#" 255 r#"
228 enum A { 256 enum A {
229 As, 257 As,
230 Bs{x:i32, y:Option<i32>}, 258 Bs { x: i32, y: Option<i32> },
231 Cs(i32, Option<i32>), 259 Cs(i32, Option<i32>),
232 } 260 }
233 fn main() { 261 fn main() {
234 match <|>A::As { 262 match A::As {
235 A::Bs{x,y:Some(_)} => {} 263 A::Bs { x, y: Some(_) } => {}
236 A::Cs(_, Some(_)) => {} 264 A::Cs(_, Some(_)) => {}
237 A::As => {} 265 $0A::As => {}
238 } 266 }
239 } 267 }
240 "#, 268 "#,
@@ -264,9 +292,9 @@ mod tests {
264 Cs(Option<i32>), 292 Cs(Option<i32>),
265 } 293 }
266 fn main() { 294 fn main() {
267 match <|>A::As { 295 match A::As {
268 A::Cs(_) | A::Bs => {} 296 A::Cs(_) | A::Bs => {}
269 A::As => {} 297 $0A::As => {}
270 } 298 }
271 } 299 }
272 "#, 300 "#,
@@ -310,11 +338,11 @@ mod tests {
310 Ys, 338 Ys,
311 } 339 }
312 fn main() { 340 fn main() {
313 match <|>A::As { 341 match A::As {
314 A::Bs if 0 < 1 => {} 342 A::Bs if 0 < 1 => {}
315 A::Ds(_value) => { let x = 1; } 343 A::Ds(_value) => { let x = 1; }
316 A::Es(B::Xs) => (), 344 A::Es(B::Xs) => (),
317 A::As => {} 345 $0A::As => {}
318 A::Cs => {} 346 A::Cs => {}
319 } 347 }
320 } 348 }
@@ -332,7 +360,7 @@ mod tests {
332 Bs, 360 Bs,
333 Cs(String), 361 Cs(String),
334 Ds(String, String), 362 Ds(String, String),
335 Es{ x: usize, y: usize } 363 Es { x: usize, y: usize }
336 } 364 }
337 365
338 fn main() { 366 fn main() {
@@ -346,13 +374,13 @@ mod tests {
346 Bs, 374 Bs,
347 Cs(String), 375 Cs(String),
348 Ds(String, String), 376 Ds(String, String),
349 Es{ x: usize, y: usize } 377 Es { x: usize, y: usize }
350 } 378 }
351 379
352 fn main() { 380 fn main() {
353 let a = A::As; 381 let a = A::As;
354 match <|>a { 382 match a {
355 A::As => {} 383 $0A::As => {}
356 A::Bs => {} 384 A::Bs => {}
357 A::Cs(_) => {} 385 A::Cs(_) => {}
358 A::Ds(_, _) => {} 386 A::Ds(_, _) => {}
@@ -368,14 +396,8 @@ mod tests {
368 check_assist( 396 check_assist(
369 fill_match_arms, 397 fill_match_arms,
370 r#" 398 r#"
371 enum A { 399 enum A { One, Two }
372 One, 400 enum B { One, Two }
373 Two,
374 }
375 enum B {
376 One,
377 Two,
378 }
379 401
380 fn main() { 402 fn main() {
381 let a = A::One; 403 let a = A::One;
@@ -384,20 +406,14 @@ mod tests {
384 } 406 }
385 "#, 407 "#,
386 r#" 408 r#"
387 enum A { 409 enum A { One, Two }
388 One, 410 enum B { One, Two }
389 Two,
390 }
391 enum B {
392 One,
393 Two,
394 }
395 411
396 fn main() { 412 fn main() {
397 let a = A::One; 413 let a = A::One;
398 let b = B::One; 414 let b = B::One;
399 match <|>(a, b) { 415 match (a, b) {
400 (A::One, B::One) => {} 416 $0(A::One, B::One) => {}
401 (A::One, B::Two) => {} 417 (A::One, B::Two) => {}
402 (A::Two, B::One) => {} 418 (A::Two, B::One) => {}
403 (A::Two, B::Two) => {} 419 (A::Two, B::Two) => {}
@@ -412,14 +428,8 @@ mod tests {
412 check_assist( 428 check_assist(
413 fill_match_arms, 429 fill_match_arms,
414 r#" 430 r#"
415 enum A { 431 enum A { One, Two }
416 One, 432 enum B { One, Two }
417 Two,
418 }
419 enum B {
420 One,
421 Two,
422 }
423 433
424 fn main() { 434 fn main() {
425 let a = A::One; 435 let a = A::One;
@@ -428,20 +438,14 @@ mod tests {
428 } 438 }
429 "#, 439 "#,
430 r#" 440 r#"
431 enum A { 441 enum A { One, Two }
432 One, 442 enum B { One, Two }
433 Two,
434 }
435 enum B {
436 One,
437 Two,
438 }
439 443
440 fn main() { 444 fn main() {
441 let a = A::One; 445 let a = A::One;
442 let b = B::One; 446 let b = B::One;
443 match <|>(&a, &b) { 447 match (&a, &b) {
444 (A::One, B::One) => {} 448 $0(A::One, B::One) => {}
445 (A::One, B::Two) => {} 449 (A::One, B::Two) => {}
446 (A::Two, B::One) => {} 450 (A::Two, B::One) => {}
447 (A::Two, B::Two) => {} 451 (A::Two, B::Two) => {}
@@ -456,14 +460,8 @@ mod tests {
456 check_assist_not_applicable( 460 check_assist_not_applicable(
457 fill_match_arms, 461 fill_match_arms,
458 r#" 462 r#"
459 enum A { 463 enum A { One, Two }
460 One, 464 enum B { One, Two }
461 Two,
462 }
463 enum B {
464 One,
465 Two,
466 }
467 465
468 fn main() { 466 fn main() {
469 let a = A::One; 467 let a = A::One;
@@ -481,14 +479,8 @@ mod tests {
481 check_assist_not_applicable( 479 check_assist_not_applicable(
482 fill_match_arms, 480 fill_match_arms,
483 r#" 481 r#"
484 enum A { 482 enum A { One, Two }
485 One, 483 enum B { One, Two }
486 Two,
487 }
488 enum B {
489 One,
490 Two,
491 }
492 484
493 fn main() { 485 fn main() {
494 let a = A::One; 486 let a = A::One;
@@ -512,10 +504,7 @@ mod tests {
512 check_assist_not_applicable( 504 check_assist_not_applicable(
513 fill_match_arms, 505 fill_match_arms,
514 r#" 506 r#"
515 enum A { 507 enum A { One, Two }
516 One,
517 Two,
518 }
519 508
520 fn main() { 509 fn main() {
521 let a = A::One; 510 let a = A::One;
@@ -531,9 +520,7 @@ mod tests {
531 check_assist( 520 check_assist(
532 fill_match_arms, 521 fill_match_arms,
533 r#" 522 r#"
534 enum A { 523 enum A { As }
535 As,
536 }
537 524
538 fn foo(a: &A) { 525 fn foo(a: &A) {
539 match a<|> { 526 match a<|> {
@@ -541,13 +528,11 @@ mod tests {
541 } 528 }
542 "#, 529 "#,
543 r#" 530 r#"
544 enum A { 531 enum A { As }
545 As,
546 }
547 532
548 fn foo(a: &A) { 533 fn foo(a: &A) {
549 match <|>a { 534 match a {
550 A::As => {} 535 $0A::As => {}
551 } 536 }
552 } 537 }
553 "#, 538 "#,
@@ -557,7 +542,7 @@ mod tests {
557 fill_match_arms, 542 fill_match_arms,
558 r#" 543 r#"
559 enum A { 544 enum A {
560 Es{ x: usize, y: usize } 545 Es { x: usize, y: usize }
561 } 546 }
562 547
563 fn foo(a: &mut A) { 548 fn foo(a: &mut A) {
@@ -567,12 +552,12 @@ mod tests {
567 "#, 552 "#,
568 r#" 553 r#"
569 enum A { 554 enum A {
570 Es{ x: usize, y: usize } 555 Es { x: usize, y: usize }
571 } 556 }
572 557
573 fn foo(a: &mut A) { 558 fn foo(a: &mut A) {
574 match <|>a { 559 match a {
575 A::Es { x, y } => {} 560 $0A::Es { x, y } => {}
576 } 561 }
577 } 562 }
578 "#, 563 "#,
@@ -611,8 +596,8 @@ mod tests {
611 enum E { X, Y } 596 enum E { X, Y }
612 597
613 fn main() { 598 fn main() {
614 match <|>E::X { 599 match E::X {
615 E::X => {} 600 $0E::X => {}
616 E::Y => {} 601 E::Y => {}
617 } 602 }
618 } 603 }
@@ -639,8 +624,8 @@ mod tests {
639 use foo::E::X; 624 use foo::E::X;
640 625
641 fn main() { 626 fn main() {
642 match <|>X { 627 match X {
643 X => {} 628 $0X => {}
644 foo::E::Y => {} 629 foo::E::Y => {}
645 } 630 }
646 } 631 }
@@ -653,10 +638,7 @@ mod tests {
653 check_assist( 638 check_assist(
654 fill_match_arms, 639 fill_match_arms,
655 r#" 640 r#"
656 enum A { 641 enum A { One, Two }
657 One,
658 Two,
659 }
660 fn foo(a: A) { 642 fn foo(a: A) {
661 match a { 643 match a {
662 // foo bar baz<|> 644 // foo bar baz<|>
@@ -666,16 +648,13 @@ mod tests {
666 } 648 }
667 "#, 649 "#,
668 r#" 650 r#"
669 enum A { 651 enum A { One, Two }
670 One,
671 Two,
672 }
673 fn foo(a: A) { 652 fn foo(a: A) {
674 match <|>a { 653 match a {
675 // foo bar baz 654 // foo bar baz
676 A::One => {} 655 A::One => {}
677 // This is where the rest should be 656 // This is where the rest should be
678 A::Two => {} 657 $0A::Two => {}
679 } 658 }
680 } 659 }
681 "#, 660 "#,
@@ -687,10 +666,7 @@ mod tests {
687 check_assist( 666 check_assist(
688 fill_match_arms, 667 fill_match_arms,
689 r#" 668 r#"
690 enum A { 669 enum A { One, Two }
691 One,
692 Two,
693 }
694 fn foo(a: A) { 670 fn foo(a: A) {
695 match a { 671 match a {
696 // foo bar baz<|> 672 // foo bar baz<|>
@@ -698,14 +674,11 @@ mod tests {
698 } 674 }
699 "#, 675 "#,
700 r#" 676 r#"
701 enum A { 677 enum A { One, Two }
702 One,
703 Two,
704 }
705 fn foo(a: A) { 678 fn foo(a: A) {
706 match <|>a { 679 match a {
707 // foo bar baz 680 // foo bar baz
708 A::One => {} 681 $0A::One => {}
709 A::Two => {} 682 A::Two => {}
710 } 683 }
711 } 684 }
@@ -728,12 +701,37 @@ mod tests {
728 r#" 701 r#"
729 enum A { One, Two, } 702 enum A { One, Two, }
730 fn foo(a: A) { 703 fn foo(a: A) {
731 match <|>a { 704 match a {
732 A::One => {} 705 $0A::One => {}
733 A::Two => {} 706 A::Two => {}
734 } 707 }
735 } 708 }
736 "#, 709 "#,
737 ); 710 );
738 } 711 }
712
713 #[test]
714 fn option_order() {
715 mark::check!(option_order);
716 let before = r#"
717fn foo(opt: Option<i32>) {
718 match opt<|> {
719 }
720}"#;
721 let before =
722 &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE);
723
724 check_assist(
725 fill_match_arms,
726 before,
727 r#"
728fn foo(opt: Option<i32>) {
729 match opt {
730 $0Some(_) => {}
731 None => {}
732 }
733}
734"#,
735 );
736 }
739} 737}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
new file mode 100644
index 000000000..9ec42f568
--- /dev/null
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -0,0 +1,559 @@
1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId;
3use ra_syntax::{
4 ast, AstNode,
5 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
6 SyntaxNode, TextRange, TextSize,
7};
8
9use crate::{AssistContext, AssistId, Assists};
10
11// FIXME: this really should be a fix for diagnostic, rather than an assist.
12
13// Assist: fix_visibility
14//