diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
commit | f05d7b41a719d848844b054a16477b29d0f063c6 (patch) | |
tree | 0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_assists/src | |
parent | 73ff610e41959e3e7c78a2b4b25b086883132956 (diff) | |
parent | 6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_assists/src')
47 files changed, 1633 insertions, 1101 deletions
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs index c0a0226fb..cda2abfb9 100644 --- a/crates/ra_assists/src/assist_config.rs +++ b/crates/ra_assists/src/assist_config.rs | |||
@@ -4,9 +4,12 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! assists if we are allowed to. | 5 | //! assists if we are allowed to. |
6 | 6 | ||
7 | use crate::AssistKind; | ||
8 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
8 | pub struct AssistConfig { | 10 | pub struct AssistConfig { |
9 | pub snippet_cap: Option<SnippetCap>, | 11 | pub snippet_cap: Option<SnippetCap>, |
12 | pub allowed: Option<Vec<AssistKind>>, | ||
10 | } | 13 | } |
11 | 14 | ||
12 | impl AssistConfig { | 15 | impl AssistConfig { |
@@ -22,6 +25,6 @@ pub struct SnippetCap { | |||
22 | 25 | ||
23 | impl Default for AssistConfig { | 26 | impl Default for AssistConfig { |
24 | fn default() -> Self { | 27 | fn default() -> Self { |
25 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } | 28 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }), allowed: None } |
26 | } | 29 | } |
27 | } | 30 | } |
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index ee614de72..3407df856 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -19,7 +19,7 @@ use ra_text_edit::TextEditBuilder; | |||
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
22 | Assist, AssistId, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 25 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
@@ -55,7 +55,6 @@ use crate::{ | |||
55 | pub(crate) struct AssistContext<'a> { | 55 | pub(crate) struct AssistContext<'a> { |
56 | pub(crate) config: &'a AssistConfig, | 56 | pub(crate) config: &'a AssistConfig, |
57 | pub(crate) sema: Semantics<'a, RootDatabase>, | 57 | pub(crate) sema: Semantics<'a, RootDatabase>, |
58 | pub(crate) db: &'a RootDatabase, | ||
59 | pub(crate) frange: FileRange, | 58 | pub(crate) frange: FileRange, |
60 | source_file: SourceFile, | 59 | source_file: SourceFile, |
61 | } | 60 | } |
@@ -67,8 +66,11 @@ impl<'a> AssistContext<'a> { | |||
67 | frange: FileRange, | 66 | frange: FileRange, |
68 | ) -> AssistContext<'a> { | 67 | ) -> AssistContext<'a> { |
69 | let source_file = sema.parse(frange.file_id); | 68 | let source_file = sema.parse(frange.file_id); |
70 | let db = sema.db; | 69 | AssistContext { config, sema, frange, source_file } |
71 | AssistContext { config, sema, db, frange, source_file } | 70 | } |
71 | |||
72 | pub(crate) fn db(&self) -> &RootDatabase { | ||
73 | self.sema.db | ||
72 | } | 74 | } |
73 | 75 | ||
74 | // NB, this ignores active selection. | 76 | // NB, this ignores active selection. |
@@ -101,14 +103,26 @@ pub(crate) struct Assists { | |||
101 | resolve: bool, | 103 | resolve: bool, |
102 | file: FileId, | 104 | file: FileId, |
103 | buf: Vec<(Assist, Option<SourceChange>)>, | 105 | buf: Vec<(Assist, Option<SourceChange>)>, |
106 | allowed: Option<Vec<AssistKind>>, | ||
104 | } | 107 | } |
105 | 108 | ||
106 | impl Assists { | 109 | impl Assists { |
107 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 110 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { |
108 | Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } | 111 | Assists { |
112 | resolve: true, | ||
113 | file: ctx.frange.file_id, | ||
114 | buf: Vec::new(), | ||
115 | allowed: ctx.config.allowed.clone(), | ||
116 | } | ||
109 | } | 117 | } |
118 | |||
110 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 119 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { |
111 | Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } | 120 | Assists { |
121 | resolve: false, | ||
122 | file: ctx.frange.file_id, | ||
123 | buf: Vec::new(), | ||
124 | allowed: ctx.config.allowed.clone(), | ||
125 | } | ||
112 | } | 126 | } |
113 | 127 | ||
114 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | 128 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { |
@@ -137,9 +151,13 @@ impl Assists { | |||
137 | target: TextRange, | 151 | target: TextRange, |
138 | f: impl FnOnce(&mut AssistBuilder), | 152 | f: impl FnOnce(&mut AssistBuilder), |
139 | ) -> Option<()> { | 153 | ) -> Option<()> { |
154 | if !self.is_allowed(&id) { | ||
155 | return None; | ||
156 | } | ||
140 | let label = Assist::new(id, label.into(), None, target); | 157 | let label = Assist::new(id, label.into(), None, target); |
141 | self.add_impl(label, f) | 158 | self.add_impl(label, f) |
142 | } | 159 | } |
160 | |||
143 | pub(crate) fn add_group( | 161 | pub(crate) fn add_group( |
144 | &mut self, | 162 | &mut self, |
145 | group: &GroupLabel, | 163 | group: &GroupLabel, |
@@ -148,9 +166,14 @@ impl Assists { | |||
148 | target: TextRange, | 166 | target: TextRange, |
149 | f: impl FnOnce(&mut AssistBuilder), | 167 | f: impl FnOnce(&mut AssistBuilder), |
150 | ) -> Option<()> { | 168 | ) -> Option<()> { |
169 | if !self.is_allowed(&id) { | ||
170 | return None; | ||
171 | } | ||
172 | |||
151 | let label = Assist::new(id, label.into(), Some(group.clone()), target); | 173 | let label = Assist::new(id, label.into(), Some(group.clone()), target); |
152 | self.add_impl(label, f) | 174 | self.add_impl(label, f) |
153 | } | 175 | } |
176 | |||
154 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 177 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
155 | let source_change = if self.resolve { | 178 | let source_change = if self.resolve { |
156 | let mut builder = AssistBuilder::new(self.file); | 179 | let mut builder = AssistBuilder::new(self.file); |
@@ -168,13 +191,20 @@ impl Assists { | |||
168 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | 191 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); |
169 | self.buf | 192 | self.buf |
170 | } | 193 | } |
194 | |||
195 | fn is_allowed(&self, id: &AssistId) -> bool { | ||
196 | match &self.allowed { | ||
197 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), | ||
198 | None => true, | ||
199 | } | ||
200 | } | ||
171 | } | 201 | } |
172 | 202 | ||
173 | pub(crate) struct AssistBuilder { | 203 | pub(crate) struct AssistBuilder { |
174 | edit: TextEditBuilder, | 204 | edit: TextEditBuilder, |
175 | file_id: FileId, | 205 | file_id: FileId, |
176 | is_snippet: bool, | 206 | is_snippet: bool, |
177 | edits: Vec<SourceFileEdit>, | 207 | change: SourceChange, |
178 | } | 208 | } |
179 | 209 | ||
180 | impl AssistBuilder { | 210 | impl AssistBuilder { |
@@ -183,7 +213,7 @@ impl AssistBuilder { | |||
183 | edit: TextEditBuilder::default(), | 213 | edit: TextEditBuilder::default(), |
184 | file_id, | 214 | file_id, |
185 | is_snippet: false, | 215 | is_snippet: false, |
186 | edits: Vec::new(), | 216 | change: SourceChange::default(), |
187 | } | 217 | } |
188 | } | 218 | } |
189 | 219 | ||
@@ -195,8 +225,8 @@ impl AssistBuilder { | |||
195 | let edit = mem::take(&mut self.edit).finish(); | 225 | let edit = mem::take(&mut self.edit).finish(); |
196 | if !edit.is_empty() { | 226 | if !edit.is_empty() { |
197 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; | 227 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; |
198 | assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id)); | 228 | assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id)); |
199 | self.edits.push(new_edit); | 229 | self.change.source_file_edits.push(new_edit); |
200 | } | 230 | } |
201 | } | 231 | } |
202 | 232 | ||
@@ -263,10 +293,10 @@ impl AssistBuilder { | |||
263 | 293 | ||
264 | fn finish(mut self) -> SourceChange { | 294 | fn finish(mut self) -> SourceChange { |
265 | self.commit(); | 295 | self.commit(); |
266 | let mut res: SourceChange = mem::take(&mut self.edits).into(); | 296 | let mut change = mem::take(&mut self.change); |
267 | if self.is_snippet { | 297 | if self.is_snippet { |
268 | res.is_snippet = true; | 298 | change.is_snippet = true; |
269 | } | 299 | } |
270 | res | 300 | change |
271 | } | 301 | } |
272 | } | 302 | } |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 00fa95b6c..5ea4f9f5b 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; | 4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | 5 | use ra_syntax::{ |
7 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
8 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
@@ -32,17 +31,17 @@ impl<'a> AstTransform<'a> for NullTransformer { | |||
32 | } | 31 | } |
33 | 32 | ||
34 | pub struct SubstituteTypeParams<'a> { | 33 | pub struct SubstituteTypeParams<'a> { |
35 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 34 | source_scope: &'a SemanticsScope<'a>, |
36 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, | 35 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, |
37 | previous: Box<dyn AstTransform<'a> + 'a>, | 36 | previous: Box<dyn AstTransform<'a> + 'a>, |
38 | } | 37 | } |
39 | 38 | ||
40 | impl<'a> SubstituteTypeParams<'a> { | 39 | impl<'a> SubstituteTypeParams<'a> { |
41 | pub fn for_trait_impl( | 40 | pub fn for_trait_impl( |
42 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 41 | source_scope: &'a SemanticsScope<'a>, |
43 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... | 42 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... |
44 | trait_: hir::Trait, | 43 | trait_: hir::Trait, |
45 | impl_def: ast::ImplDef, | 44 | impl_def: ast::Impl, |
46 | ) -> SubstituteTypeParams<'a> { | 45 | ) -> SubstituteTypeParams<'a> { |
47 | let substs = get_syntactic_substs(impl_def).unwrap_or_default(); | 46 | let substs = get_syntactic_substs(impl_def).unwrap_or_default(); |
48 | let generic_def: hir::GenericDef = trait_.into(); | 47 | let generic_def: hir::GenericDef = trait_.into(); |
@@ -81,7 +80,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
81 | 80 | ||
82 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the | 81 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the |
83 | // trait ref, and then go from the types in the substs back to the syntax) | 82 | // trait ref, and then go from the types in the substs back to the syntax) |
84 | fn get_syntactic_substs(impl_def: ast::ImplDef) -> Option<Vec<ast::TypeRef>> { | 83 | fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::TypeRef>> { |
85 | let target_trait = impl_def.target_trait()?; | 84 | let target_trait = impl_def.target_trait()?; |
86 | let path_type = match target_trait { | 85 | let path_type = match target_trait { |
87 | ast::TypeRef::PathType(path) => path, | 86 | ast::TypeRef::PathType(path) => path, |
@@ -126,16 +125,13 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | |||
126 | } | 125 | } |
127 | 126 | ||
128 | pub struct QualifyPaths<'a> { | 127 | pub struct QualifyPaths<'a> { |
129 | target_scope: &'a SemanticsScope<'a, RootDatabase>, | 128 | target_scope: &'a SemanticsScope<'a>, |
130 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 129 | source_scope: &'a SemanticsScope<'a>, |
131 | previous: Box<dyn AstTransform<'a> + 'a>, | 130 | previous: Box<dyn AstTransform<'a> + 'a>, |
132 | } | 131 | } |
133 | 132 | ||
134 | impl<'a> QualifyPaths<'a> { | 133 | impl<'a> QualifyPaths<'a> { |
135 | pub fn new( | 134 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { |
136 | target_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
137 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
138 | ) -> Self { | ||
139 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } | 135 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } |
140 | } | 136 | } |
141 | 137 | ||
@@ -156,7 +152,7 @@ impl<'a> QualifyPaths<'a> { | |||
156 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; | 152 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; |
157 | match resolution { | 153 | match resolution { |
158 | PathResolution::Def(def) => { | 154 | PathResolution::Def(def) => { |
159 | let found_path = from.find_use_path(self.source_scope.db, def)?; | 155 | let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; |
160 | let mut path = path_to_ast(found_path); | 156 | let mut path = path_to_ast(found_path); |
161 | 157 | ||
162 | let type_args = p | 158 | let type_args = p |
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index fa70c8496..b67438b6b 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -8,7 +8,7 @@ use stdx::SepBy; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 10 | assist_context::{AssistContext, Assists}, |
11 | AssistId, | 11 | AssistId, AssistKind, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | // Assist: add_custom_impl | 14 | // Assist: add_custom_impl |
@@ -29,8 +29,8 @@ use crate::{ | |||
29 | // } | 29 | // } |
30 | // ``` | 30 | // ``` |
31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
32 | let input = ctx.find_node_at_offset::<ast::AttrInput>()?; | 32 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; |
33 | let attr = input.syntax().parent().and_then(ast::Attr::cast)?; | 33 | let input = attr.token_tree()?; |
34 | 34 | ||
35 | let attr_name = attr | 35 | let attr_name = attr |
36 | .syntax() | 36 | .syntax() |
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); |
53 | 53 | ||
54 | let target = attr.syntax().text_range(); | 54 | let target = attr.syntax().text_range(); |
55 | acc.add(AssistId("add_custom_impl"), label, target, |builder| { | 55 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { |
56 | let new_attr_input = input | 56 | let new_attr_input = input |
57 | .syntax() | 57 | .syntax() |
58 | .descendants_with_tokens() | 58 | .descendants_with_tokens() |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 90b06a625..e69f0a89b 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner}, | 3 | ast::{self, AstNode, LetStmt, NameOwner}, |
4 | TextRange, | 4 | TextRange, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
8 | 8 | ||
9 | // Assist: add_explicit_type | 9 | // Assist: add_explicit_type |
10 | // | 10 | // |
@@ -22,11 +22,11 @@ use crate::{AssistContext, AssistId, Assists}; | |||
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let let_stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | 26 | let module = ctx.sema.scope(let_stmt.syntax()).module()?; |
27 | let expr = stmt.initializer()?; | 27 | let expr = let_stmt.initializer()?; |
28 | // Must be a binding | 28 | // Must be a binding |
29 | let pat = match stmt.pat()? { | 29 | let pat = match let_stmt.pat()? { |
30 | ast::Pat::BindPat(bind_pat) => bind_pat, | 30 | ast::Pat::BindPat(bind_pat) => bind_pat, |
31 | _ => return None, | 31 | _ => return None, |
32 | }; | 32 | }; |
@@ -34,8 +34,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
34 | // The binding must have a name | 34 | // The binding must have a name |
35 | let name = pat.name()?; | 35 | let name = pat.name()?; |
36 | let name_range = name.syntax().text_range(); | 36 | let name_range = name.syntax().text_range(); |
37 | let stmt_range = stmt.syntax().text_range(); | 37 | let stmt_range = let_stmt.syntax().text_range(); |
38 | let eq_range = stmt.eq_token()?.text_range(); | 38 | let eq_range = let_stmt.eq_token()?.text_range(); |
39 | // Assist should only be applicable if cursor is between 'let' and '=' | 39 | // Assist should only be applicable if cursor is between 'let' and '=' |
40 | let let_range = TextRange::new(stmt_range.start(), eq_range.start()); | 40 | let let_range = TextRange::new(stmt_range.start(), eq_range.start()); |
41 | let cursor_in_range = let_range.contains_range(ctx.frange.range); | 41 | let cursor_in_range = let_range.contains_range(ctx.frange.range); |
@@ -44,7 +44,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
44 | } | 44 | } |
45 | // Assist not applicable if the type has already been specified | 45 | // Assist not applicable if the type has already been specified |
46 | // and it has no placeholders | 46 | // and it has no placeholders |
47 | let ascribed_ty = stmt.ascribed_type(); | 47 | let ascribed_ty = let_stmt.ty(); |
48 | if let Some(ty) = &ascribed_ty { | 48 | if let Some(ty) = &ascribed_ty { |
49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { | 49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { |
50 | return None; | 50 | return None; |
@@ -57,9 +57,9 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
57 | return None; | 57 | return None; |
58 | } | 58 | } |
59 | 59 | ||
60 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; | 60 | let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; |
61 | acc.add( | 61 | acc.add( |
62 | AssistId("add_explicit_type"), | 62 | AssistId("add_explicit_type", AssistKind::RefactorRewrite), |
63 | format!("Insert explicit type `{}`", inferred_type), | 63 | format!("Insert explicit type `{}`", inferred_type), |
64 | pat_range, | 64 | pat_range, |
65 | |builder| match ascribed_ty { | 65 | |builder| match ascribed_ty { |
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs deleted file mode 100644 index eceba7d0a..000000000 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
1 | use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner}; | ||
2 | use stdx::{format_to, SepBy}; | ||
3 | |||
4 | use crate::{AssistContext, AssistId, Assists}; | ||
5 | |||
6 | // Assist: add_impl | ||
7 | // | ||
8 | // Adds a new inherent impl for a type. | ||
9 | // | ||
10 | // ``` | ||
11 | // struct Ctx<T: Clone> { | ||
12 | // data: T,<|> | ||
13 | // } | ||
14 | // ``` | ||
15 | // -> | ||
16 | // ``` | ||
17 | // struct Ctx<T: Clone> { | ||
18 | // data: T, | ||
19 | // } | ||
20 | // | ||
21 | // impl<T: Clone> Ctx<T> { | ||
22 | // $0 | ||
23 | // } | ||
24 | // ``` | ||
25 | pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
26 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | ||
27 | let name = nominal.name()?; | ||
28 | let target = nominal.syntax().text_range(); | ||
29 | acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| { | ||
30 | let type_params = nominal.type_param_list(); | ||
31 | let start_offset = nominal.syntax().text_range().end(); | ||
32 | let mut buf = String::new(); | ||
33 | buf.push_str("\n\nimpl"); | ||
34 | if let Some(type_params) = &type_params { | ||
35 | format_to!(buf, "{}", type_params.syntax()); | ||
36 | } | ||
37 | buf.push_str(" "); | ||
38 | buf.push_str(name.text().as_str()); | ||
39 | if let Some(type_params) = type_params { | ||
40 | let lifetime_params = type_params | ||
41 | .lifetime_params() | ||
42 | .filter_map(|it| it.lifetime_token()) | ||
43 | .map(|it| it.text().clone()); | ||
44 | let type_params = | ||
45 | type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); | ||
46 | |||
47 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); | ||
48 | format_to!(buf, "<{}>", generic_params) | ||
49 | } | ||
50 | match ctx.config.snippet_cap { | ||
51 | Some(cap) => { | ||
52 | buf.push_str(" {\n $0\n}"); | ||
53 | edit.insert_snippet(cap, start_offset, buf); | ||
54 | } | ||
55 | None => { | ||
56 | buf.push_str(" {\n}"); | ||
57 | edit.insert(start_offset, buf); | ||
58 | } | ||
59 | } | ||
60 | }) | ||
61 | } | ||
62 | |||
63 | #[cfg(test)] | ||
64 | mod tests { | ||
65 | use crate::tests::{check_assist, check_assist_target}; | ||
66 | |||
67 | use super::*; | ||
68 | |||
69 | #[test] | ||
70 | fn test_add_impl() { | ||
71 | check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n"); | ||
72 | check_assist( | ||
73 | add_impl, | ||
74 | "struct Foo<T: Clone> {<|>}", | ||
75 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}", | ||
76 | ); | ||
77 | check_assist( | ||
78 | add_impl, | ||
79 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
80 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", | ||
81 | ); | ||
82 | } | ||
83 | |||
84 | #[test] | ||
85 | fn add_impl_target() { | ||
86 | check_assist_target( | ||
87 | add_impl, | ||
88 | " | ||
89 | struct SomeThingIrrelevant; | ||
90 | /// Has a lifetime parameter | ||
91 | struct Foo<'a, T: Foo<'a>> {<|>} | ||
92 | struct EvenMoreIrrelevant; | ||
93 | ", | ||
94 | "/// Has a lifetime parameter | ||
95 | struct Foo<'a, T: Foo<'a>> {}", | ||
96 | ); | ||
97 | } | ||
98 | } | ||
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index abacd4065..95a750aee 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -12,7 +12,7 @@ use crate::{ | |||
12 | assist_context::{AssistContext, Assists}, | 12 | assist_context::{AssistContext, Assists}, |
13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
14 | utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, | 14 | utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, |
15 | AssistId, | 15 | AssistId, AssistKind, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | #[derive(PartialEq)] | 18 | #[derive(PartialEq)] |
@@ -111,16 +111,17 @@ fn add_missing_impl_members_inner( | |||
111 | label: &'static str, | 111 | label: &'static str, |
112 | ) -> Option<()> { | 112 | ) -> Option<()> { |
113 | let _p = ra_prof::profile("add_missing_impl_members_inner"); | 113 | let _p = ra_prof::profile("add_missing_impl_members_inner"); |
114 | let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?; | 114 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; |
115 | let impl_item_list = impl_def.item_list()?; | 115 | let impl_item_list = impl_def.assoc_item_list()?; |
116 | 116 | ||
117 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; | 117 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; |
118 | 118 | ||
119 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { | 119 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { |
120 | match item { | 120 | match item { |
121 | ast::AssocItem::FnDef(def) => def.name(), | 121 | ast::AssocItem::Fn(def) => def.name(), |
122 | ast::AssocItem::TypeAliasDef(def) => def.name(), | 122 | ast::AssocItem::TypeAlias(def) => def.name(), |
123 | ast::AssocItem::ConstDef(def) => def.name(), | 123 | ast::AssocItem::Const(def) => def.name(), |
124 | ast::AssocItem::MacroCall(_) => None, | ||
124 | } | 125 | } |
125 | .map(|it| it.text().clone()) | 126 | .map(|it| it.text().clone()) |
126 | }; | 127 | }; |
@@ -128,13 +129,13 @@ fn add_missing_impl_members_inner( | |||
128 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) | 129 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) |
129 | .iter() | 130 | .iter() |
130 | .map(|i| match i { | 131 | .map(|i| match i { |
131 | hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), | 132 | hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value), |
132 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), | 133 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value), |
133 | hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), | 134 | hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value), |
134 | }) | 135 | }) |
135 | .filter(|t| def_name(&t).is_some()) | 136 | .filter(|t| def_name(&t).is_some()) |
136 | .filter(|t| match t { | 137 | .filter(|t| match t { |
137 | ast::AssocItem::FnDef(def) => match mode { | 138 | ast::AssocItem::Fn(def) => match mode { |
138 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | 139 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), |
139 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | 140 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), |
140 | }, | 141 | }, |
@@ -147,7 +148,7 @@ fn add_missing_impl_members_inner( | |||
147 | } | 148 | } |
148 | 149 | ||
149 | let target = impl_def.syntax().text_range(); | 150 | let target = impl_def.syntax().text_range(); |
150 | acc.add(AssistId(assist_id), label, target, |builder| { | 151 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { |
151 | let n_existing_items = impl_item_list.assoc_items().count(); | 152 | let n_existing_items = impl_item_list.assoc_items().count(); |
152 | let source_scope = ctx.sema.scope_for_def(trait_); | 153 | let source_scope = ctx.sema.scope_for_def(trait_); |
153 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); | 154 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); |
@@ -157,7 +158,8 @@ fn add_missing_impl_members_inner( | |||
157 | .into_iter() | 158 | .into_iter() |
158 | .map(|it| ast_transform::apply(&*ast_transform, it)) | 159 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
159 | .map(|it| match it { | 160 | .map(|it| match it { |
160 | ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), | 161 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), |
162 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | ||
161 | _ => it, | 163 | _ => it, |
162 | }) | 164 | }) |
163 | .map(|it| edit::remove_attrs_and_docs(&it)); | 165 | .map(|it| edit::remove_attrs_and_docs(&it)); |
@@ -170,7 +172,7 @@ fn add_missing_impl_members_inner( | |||
170 | Some(cap) => { | 172 | Some(cap) => { |
171 | let mut cursor = Cursor::Before(first_new_item.syntax()); | 173 | let mut cursor = Cursor::Before(first_new_item.syntax()); |
172 | let placeholder; | 174 | let placeholder; |
173 | if let ast::AssocItem::FnDef(func) = &first_new_item { | 175 | if let ast::AssocItem::Fn(func) = &first_new_item { |
174 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { | 176 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { |
175 | if m.syntax().text() == "todo!()" { | 177 | if m.syntax().text() == "todo!()" { |
176 | placeholder = m; | 178 | placeholder = m; |
@@ -188,7 +190,7 @@ fn add_missing_impl_members_inner( | |||
188 | }) | 190 | }) |
189 | } | 191 | } |
190 | 192 | ||
191 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | 193 | fn add_body(fn_def: ast::Fn) -> ast::Fn { |
192 | if fn_def.body().is_some() { | 194 | if fn_def.body().is_some() { |
193 | return fn_def; | 195 | return fn_def; |
194 | } | 196 | } |
@@ -684,4 +686,26 @@ impl Foo<T> for S<T> { | |||
684 | }"#, | 686 | }"#, |
685 | ) | 687 | ) |
686 | } | 688 | } |
689 | |||
690 | #[test] | ||
691 | fn test_assoc_type_bounds_are_removed() { | ||
692 | check_assist( | ||
693 | add_missing_impl_members, | ||
694 | r#" | ||
695 | trait Tr { | ||
696 | type Ty: Copy + 'static; | ||
697 | } | ||
698 | |||
699 | impl Tr for ()<|> { | ||
700 | }"#, | ||
701 | r#" | ||
702 | trait Tr { | ||
703 | type Ty: Copy + 'static; | ||
704 | } | ||
705 | |||
706 | impl Tr for () { | ||
707 | $0type Ty; | ||
708 | }"#, | ||
709 | ) | ||
710 | } | ||
687 | } | 711 | } |
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs index 26acf81f2..0c565e89a 100644 --- a/crates/ra_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs | |||
@@ -4,7 +4,7 @@ use test_utils::mark; | |||
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | assist_context::{AssistContext, Assists}, | 6 | assist_context::{AssistContext, Assists}, |
7 | AssistId, | 7 | AssistId, AssistKind, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | // Assist: add_turbo_fish | 10 | // Assist: add_turbo_fish |
@@ -25,7 +25,14 @@ use crate::{ | |||
25 | // } | 25 | // } |
26 | // ``` | 26 | // ``` |
27 | pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 27 | pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
28 | let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?; | 28 | let ident = ctx.find_token_at_offset(SyntaxKind::IDENT).or_else(|| { |
29 | let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?; | ||
30 | if arg_list.args().count() > 0 { | ||
31 | return None; | ||
32 | } | ||
33 | mark::hit!(add_turbo_fish_after_call); | ||
34 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) | ||
35 | })?; | ||
29 | let next_token = ident.next_token()?; | 36 | let next_token = ident.next_token()?; |
30 | if next_token.kind() == T![::] { | 37 | if next_token.kind() == T![::] { |
31 | mark::hit!(add_turbo_fish_one_fish_is_enough); | 38 | mark::hit!(add_turbo_fish_one_fish_is_enough); |
@@ -45,12 +52,15 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
45 | mark::hit!(add_turbo_fish_non_generic); | 52 | mark::hit!(add_turbo_fish_non_generic); |
46 | return None; | 53 | return None; |
47 | } | 54 | } |
48 | acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { | 55 | acc.add( |
49 | match ctx.config.snippet_cap { | 56 | AssistId("add_turbo_fish", AssistKind::RefactorRewrite), |
57 | "Add `::<>`", | ||
58 | ident.text_range(), | ||
59 | |builder| match ctx.config.snippet_cap { | ||
50 | Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), | 60 | Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), |
51 | None => builder.insert(ident.text_range().end(), "::<_>"), | 61 | None => builder.insert(ident.text_range().end(), "::<_>"), |
52 | } | 62 | }, |
53 | }) | 63 | ) |
54 | } | 64 | } |
55 | 65 | ||
56 | #[cfg(test)] | 66 | #[cfg(test)] |
@@ -80,6 +90,26 @@ fn main() { | |||
80 | } | 90 | } |
81 | 91 | ||
82 | #[test] | 92 | #[test] |
93 | fn add_turbo_fish_after_call() { | ||
94 | mark::check!(add_turbo_fish_after_call); | ||
95 | check_assist( | ||
96 | add_turbo_fish, | ||
97 | r#" | ||
98 | fn make<T>() -> T {} | ||
99 | fn main() { | ||
100 | make()<|>; | ||
101 | } | ||
102 | "#, | ||
103 | r#" | ||
104 | fn make<T>() -> T {} | ||
105 | fn main() { | ||
106 | make::<${0:_}>(); | ||
107 | } | ||
108 | "#, | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
83 | fn add_turbo_fish_method() { | 113 | fn add_turbo_fish_method() { |
84 | check_assist( | 114 | check_assist( |
85 | add_turbo_fish, | 115 | add_turbo_fish, |
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index 233e8fb8e..de701f8b8 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::ast::{self, AstNode}; | 1 | use ra_syntax::ast::{self, AstNode}; |
2 | 2 | ||
3 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; | 3 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
5 | // Assist: apply_demorgan | 5 | // Assist: apply_demorgan |
6 | // | 6 | // |
@@ -39,11 +39,16 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
39 | let rhs_range = rhs.syntax().text_range(); | 39 | let rhs_range = rhs.syntax().text_range(); |
40 | let not_rhs = invert_boolean_expression(rhs); | 40 | let not_rhs = invert_boolean_expression(rhs); |
41 | 41 | ||
42 | acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { | 42 | acc.add( |
43 | edit.replace(op_range, opposite_op); | 43 | AssistId("apply_demorgan", AssistKind::RefactorRewrite), |
44 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); | 44 | "Apply De Morgan's law", |
45 | edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); | 45 | op_range, |
46 | }) | 46 | |edit| { |
47 | edit.replace(op_range, opposite_op); | ||
48 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); | ||
49 | edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); | ||
50 | }, | ||
51 | ) | ||
47 | } | 52 | } |
48 | 53 | ||
49 | // Return the opposite text for a given logical operator, if it makes sense | 54 | // Return the opposite text for a given logical operator, if it makes sense |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index d1cafa7d9..01e7b7a44 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -5,7 +5,7 @@ use hir::{ | |||
5 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, | 5 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, |
6 | Type, | 6 | Type, |
7 | }; | 7 | }; |
8 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | 8 | use ra_ide_db::{imports_locator, RootDatabase}; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | ast::{self, AstNode}, | 11 | ast::{self, AstNode}, |
@@ -13,7 +13,9 @@ use ra_syntax::{ | |||
13 | }; | 13 | }; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | 15 | ||
16 | use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; | 16 | use crate::{ |
17 | utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel, | ||
18 | }; | ||
17 | 19 | ||
18 | // Assist: auto_import | 20 | // Assist: auto_import |
19 | // | 21 | // |
@@ -35,8 +37,8 @@ use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, Group | |||
35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 37 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
36 | // ``` | 38 | // ``` |
37 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 39 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; | 40 | let auto_import_assets = AutoImportAssets::new(ctx)?; |
39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); | 41 | let proposed_imports = auto_import_assets.search_for_imports(ctx); |
40 | if proposed_imports.is_empty() { | 42 | if proposed_imports.is_empty() { |
41 | return None; | 43 | return None; |
42 | } | 44 | } |
@@ -46,7 +48,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
46 | for import in proposed_imports { | 48 | for import in proposed_imports { |
47 | acc.add_group( | 49 | acc.add_group( |
48 | &group, | 50 | &group, |
49 | AssistId("auto_import"), | 51 | AssistId("auto_import", AssistKind::QuickFix), |
50 | format!("Import `{}`", &import), | 52 | format!("Import `{}`", &import), |
51 | range, | 53 | range, |
52 | |builder| { | 54 | |builder| { |
@@ -90,7 +92,7 @@ impl AutoImportAssets { | |||
90 | 92 | ||
91 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> { | 93 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> { |
92 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | 94 | let syntax_under_caret = path_under_caret.syntax().to_owned(); |
93 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { | 95 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { |
94 | return None; | 96 | return None; |
95 | } | 97 | } |
96 | 98 | ||
@@ -127,11 +129,11 @@ impl AutoImportAssets { | |||
127 | GroupLabel(name) | 129 | GroupLabel(name) |
128 | } | 130 | } |
129 | 131 | ||
130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | 132 | fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> { |
131 | let _p = profile("auto_import::search_for_imports"); | 133 | let _p = profile("auto_import::search_for_imports"); |
134 | let db = ctx.db(); | ||
132 | let current_crate = self.module_with_name_to_import.krate(); | 135 | let current_crate = self.module_with_name_to_import.krate(); |
133 | ImportsLocator::new(db, current_crate) | 136 | imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query()) |
134 | .find_imports(&self.get_search_query()) | ||
135 | .into_iter() | 137 | .into_iter() |
136 | .filter_map(|candidate| match &self.import_candidate { | 138 | .filter_map(|candidate| match &self.import_candidate { |
137 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | 139 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { |
@@ -811,6 +813,146 @@ fn main() { | |||
811 | } | 813 | } |
812 | 814 | ||
813 | #[test] | 815 | #[test] |
816 | fn trait_method_cross_crate() { | ||
817 | check_assist( | ||
818 | auto_import, | ||
819 | r" | ||
820 | //- /main.rs crate:main deps:dep | ||
821 | fn main() { | ||
822 | let test_struct = dep::test_mod::TestStruct {}; | ||
823 | test_struct.test_meth<|>od() | ||
824 | } | ||
825 | //- /dep.rs crate:dep | ||
826 | pub mod test_mod { | ||
827 | pub trait TestTrait { | ||
828 | fn test_method(&self); | ||
829 | } | ||
830 | pub struct TestStruct {} | ||
831 | impl TestTrait for TestStruct { | ||
832 | fn test_method(&self) {} | ||
833 | } | ||
834 | } | ||
835 | ", | ||
836 | r" | ||
837 | use dep::test_mod::TestTrait; | ||
838 | |||
839 | fn main() { | ||
840 | let test_struct = dep::test_mod::TestStruct {}; | ||
841 | test_struct.test_method() | ||
842 | } | ||
843 | ", | ||
844 | ); | ||
845 | } | ||
846 | |||
847 | #[test] | ||
848 | fn assoc_fn_cross_crate() { | ||
849 | check_assist( | ||
850 | auto_import, | ||
851 | r" | ||
852 | //- /main.rs crate:main deps:dep | ||
853 | fn main() { | ||
854 | dep::test_mod::TestStruct::test_func<|>tion | ||
855 | } | ||
856 | //- /dep.rs crate:dep | ||
857 | pub mod test_mod { | ||
858 | pub trait TestTrait { | ||
859 | fn test_function(); | ||
860 | } | ||
861 | pub struct TestStruct {} | ||
862 | impl TestTrait for TestStruct { | ||
863 | fn test_function() {} | ||
864 | } | ||
865 | } | ||
866 | ", | ||
867 | r" | ||
868 | use dep::test_mod::TestTrait; | ||
869 | |||
870 | fn main() { | ||
871 | dep::test_mod::TestStruct::test_function | ||
872 | } | ||
873 | ", | ||
874 | ); | ||
875 | } | ||
876 | |||
877 | #[test] | ||
878 | fn assoc_const_cross_crate() { | ||
879 | check_assist( | ||
880 | auto_import, | ||
881 | r" | ||
882 | //- /main.rs crate:main deps:dep | ||
883 | fn main() { | ||
884 | dep::test_mod::TestStruct::CONST<|> | ||
885 | } | ||
886 | //- /dep.rs crate:dep | ||
887 | pub mod test_mod { | ||
888 | pub trait TestTrait { | ||
889 | const CONST: bool; | ||
890 | } | ||
891 | pub struct TestStruct {} | ||
892 | impl TestTrait for TestStruct { | ||
893 | const CONST: bool = true; | ||
894 | } | ||
895 | } | ||
896 | ", | ||
897 | r" | ||
898 | use dep::test_mod::TestTrait; | ||
899 | |||
900 | fn main() { | ||
901 | dep::test_mod::TestStruct::CONST | ||
902 | } | ||
903 | ", | ||
904 | ); | ||
905 | } | ||
906 | |||
907 | #[test] | ||
908 | fn assoc_fn_as_method_cross_crate() { | ||
909 | check_assist_not_applicable( | ||
910 | auto_import, | ||
911 | r" | ||
912 | //- /main.rs crate:main deps:dep | ||
913 | fn main() { | ||
914 | let test_struct = dep::test_mod::TestStruct {}; | ||
915 | test_struct.test_func<|>tion() | ||
916 | } | ||
917 | //- /dep.rs crate:dep | ||
918 | pub mod test_mod { | ||
919 | pub trait TestTrait { | ||
920 | fn test_function(); | ||
921 | } | ||
922 | pub struct TestStruct {} | ||
923 | impl TestTrait for TestStruct { | ||
924 | fn test_function() {} | ||
925 | } | ||
926 | } | ||
927 | ", | ||
928 | ); | ||
929 | } | ||
930 | |||
931 | #[test] | ||
932 | fn private_trait_cross_crate() { | ||
933 | check_assist_not_applicable( | ||
934 | auto_import, | ||
935 | r" | ||
936 | //- /main.rs crate:main deps:dep | ||
937 | fn main() { | ||
938 | let test_struct = dep::test_mod::TestStruct {}; | ||
939 | test_struct.test_meth<|>od() | ||
940 | } | ||
941 | //- /dep.rs crate:dep | ||
942 | pub mod test_mod { | ||
943 | trait TestTrait { | ||
944 | fn test_method(&self); | ||
945 | } | ||
946 | pub struct TestStruct {} | ||
947 | impl TestTrait for TestStruct { | ||
948 | fn test_method(&self) {} | ||
949 | } | ||
950 | } | ||
951 | ", | ||
952 | ); | ||
953 | } | ||
954 | |||
955 | #[test] | ||
814 | fn not_applicable_for_imported_trait_for_method() { | 956 | fn not_applicable_for_imported_trait_for_method() { |
815 | check_assist_not_applicable( | 957 | check_assist_not_applicable( |
816 | auto_import, | 958 | auto_import, |
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 855baf187..167e162d8 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | AstNode, SyntaxNode, | 3 | AstNode, SyntaxNode, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | 8 | ||
9 | // Assist: change_return_type_to_result | 9 | // Assist: change_return_type_to_result |
@@ -20,9 +20,9 @@ use test_utils::mark; | |||
20 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 20 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
21 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; | 21 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; |
22 | // FIXME: extend to lambdas as well | 22 | // FIXME: extend to lambdas as well |
23 | let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; | 23 | let fn_def = ret_type.syntax().parent().and_then(ast::Fn::cast)?; |
24 | 24 | ||
25 | let type_ref = &ret_type.type_ref()?; | 25 | let type_ref = &ret_type.ty()?; |
26 | let ret_type_str = type_ref.syntax().text().to_string(); | 26 | let ret_type_str = type_ref.syntax().text().to_string(); |
27 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); | 27 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); |
28 | if let Some(ret_type_first_part) = first_part_ret_type { | 28 | if let Some(ret_type_first_part) = first_part_ret_type { |
@@ -35,8 +35,8 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex | |||
35 | let block_expr = &fn_def.body()?; | 35 | let block_expr = &fn_def.body()?; |
36 | 36 | ||
37 | acc.add( | 37 | acc.add( |
38 | AssistId("change_return_type_to_result"), | 38 | AssistId("change_return_type_to_result", AssistKind::RefactorRewrite), |
39 | "Change return type to Result", | 39 | "Wrap return type in Result", |
40 | type_ref.syntax().text_range(), | 40 | type_ref.syntax().text_range(), |
41 | |builder| { | 41 | |builder| { |
42 | let mut tail_return_expr_collector = TailReturnCollector::new(); | 42 | let mut tail_return_expr_collector = TailReturnCollector::new(); |
@@ -240,7 +240,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | |||
240 | Expr::ParenExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 240 | Expr::ParenExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
241 | Expr::PathExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 241 | Expr::PathExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
242 | Expr::Label(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 242 | Expr::Label(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
243 | Expr::RecordLit(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 243 | Expr::RecordExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
244 | Expr::IndexExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 244 | Expr::IndexExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
245 | Expr::MethodCallExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 245 | Expr::MethodCallExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
246 | Expr::AwaitExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | 246 | Expr::AwaitExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), |
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 157c7b665..724daa93f 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast::{self, NameOwner, VisibilityOwner}, | 2 | ast::{self, NameOwner, VisibilityOwner}, |
3 | AstNode, | 3 | AstNode, |
4 | SyntaxKind::{CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY}, | 4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, VISIBILITY}, |
5 | T, | 5 | T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | 8 | ||
9 | use crate::{utils::vis_offset, AssistContext, AssistId, Assists}; | 9 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; |
10 | 10 | ||
11 | // Assist: change_visibility | 11 | // Assist: change_visibility |
12 | // | 12 | // |
@@ -28,12 +28,15 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
28 | 28 | ||
29 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 29 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
30 | let item_keyword = ctx.token_at_offset().find(|leaf| { | 30 | let item_keyword = ctx.token_at_offset().find(|leaf| { |
31 | matches!(leaf.kind(), T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait]) | 31 | matches!( |
32 | leaf.kind(), | ||
33 | T![const] | T![static] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] | ||
34 | ) | ||
32 | }); | 35 | }); |
33 | 36 | ||
34 | let (offset, target) = if let Some(keyword) = item_keyword { | 37 | let (offset, target) = if let Some(keyword) = item_keyword { |
35 | let parent = keyword.parent(); | 38 | let parent = keyword.parent(); |
36 | let def_kws = vec![CONST_DEF, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; | 39 | let def_kws = vec![CONST, STATIC, FN, MODULE, STRUCT, ENUM, TRAIT]; |
37 | // Parent is not a definition, can't add visibility | 40 | // Parent is not a definition, can't add visibility |
38 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { | 41 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { |
39 | return None; | 42 | return None; |
@@ -44,7 +47,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
44 | } | 47 | } |
45 | (vis_offset(&parent), keyword.text_range()) | 48 | (vis_offset(&parent), keyword.text_range()) |
46 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { | 49 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { |
47 | let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; | 50 | let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; |
48 | if field.name()? != field_name { | 51 | if field.name()? != field_name { |
49 | mark::hit!(change_visibility_field_false_positive); | 52 | mark::hit!(change_visibility_field_false_positive); |
50 | return None; | 53 | return None; |
@@ -53,7 +56,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
53 | return None; | 56 | return None; |
54 | } | 57 | } |
55 | (vis_offset(field.syntax()), field_name.syntax().text_range()) | 58 | (vis_offset(field.syntax()), field_name.syntax().text_range()) |
56 | } else if let Some(field) = ctx.find_node_at_offset::<ast::TupleFieldDef>() { | 59 | } else if let Some(field) = ctx.find_node_at_offset::<ast::TupleField>() { |
57 | if field.visibility().is_some() { | 60 | if field.visibility().is_some() { |
58 | return None; | 61 | return None; |
59 | } | 62 | } |
@@ -62,16 +65,21 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
62 | return None; | 65 | return None; |
63 | }; | 66 | }; |
64 | 67 | ||
65 | acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { | 68 | acc.add( |
66 | edit.insert(offset, "pub(crate) "); | 69 | AssistId("change_visibility", AssistKind::RefactorRewrite), |
67 | }) | 70 | "Change visibility to pub(crate)", |
71 | target, | ||
72 | |edit| { | ||
73 | edit.insert(offset, "pub(crate) "); | ||
74 | }, | ||
75 | ) | ||
68 | } | 76 | } |
69 | 77 | ||
70 | fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | 78 | fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { |
71 | if vis.syntax().text() == "pub" { | 79 | if vis.syntax().text() == "pub" { |
72 | let target = vis.syntax().text_range(); | 80 | let target = vis.syntax().text_range(); |
73 | return acc.add( | 81 | return acc.add( |
74 | AssistId("change_visibility"), | 82 | AssistId("change_visibility", AssistKind::RefactorRewrite), |
75 | "Change Visibility to pub(crate)", | 83 | "Change Visibility to pub(crate)", |
76 | target, | 84 | target, |
77 | |edit| { | 85 | |edit| { |
@@ -82,7 +90,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
82 | if vis.syntax().text() == "pub(crate)" { | 90 | if vis.syntax().text() == "pub(crate)" { |
83 | let target = vis.syntax().text_range(); | 91 | let target = vis.syntax().text_range(); |
84 | return acc.add( | 92 | return acc.add( |
85 | AssistId("change_visibility"), | 93 | AssistId("change_visibility", AssistKind::RefactorRewrite), |
86 | "Change visibility to pub", | 94 | "Change visibility to pub", |
87 | target, | 95 | target, |
88 | |edit| { | 96 | |edit| { |
@@ -147,6 +155,11 @@ mod tests { | |||
147 | } | 155 | } |
148 | 156 | ||
149 | #[test] | 157 | #[test] |
158 | fn change_visibility_static() { | ||
159 | check_assist(change_visibility, "<|>static FOO = 3u8;", "pub(crate) static FOO = 3u8;"); | ||
160 | } | ||
161 | |||
162 | #[test] | ||
150 | fn change_visibility_handles_comment_attrs() { | 163 | fn change_visibility_handles_comment_attrs() { |
151 | check_assist( | 164 | check_assist( |
152 | change_visibility, | 165 | change_visibility, |
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index dfade7432..3650289fd 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -8,14 +8,14 @@ use ra_syntax::{ | |||
8 | make, | 8 | make, |
9 | }, | 9 | }, |
10 | AstNode, | 10 | AstNode, |
11 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, | 11 | SyntaxKind::{FN, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, |
12 | SyntaxNode, | 12 | SyntaxNode, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | assist_context::{AssistContext, Assists}, | 16 | assist_context::{AssistContext, Assists}, |
17 | utils::invert_boolean_expression, | 17 | utils::invert_boolean_expression, |
18 | AssistId, | 18 | AssistId, AssistKind, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | // Assist: convert_to_guarded_return | 21 | // Assist: convert_to_guarded_return |
@@ -88,7 +88,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
88 | 88 | ||
89 | let early_expression: ast::Expr = match parent_container.kind() { | 89 | let early_expression: ast::Expr = match parent_container.kind() { |
90 | WHILE_EXPR | LOOP_EXPR => make::expr_continue(), | 90 | WHILE_EXPR | LOOP_EXPR => make::expr_continue(), |
91 | FN_DEF => make::expr_return(), | 91 | FN => make::expr_return(), |
92 | _ => return None, | 92 | _ => return None, |
93 | }; | 93 | }; |
94 | 94 | ||
@@ -99,86 +99,92 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
99 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; | 99 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; |
100 | 100 | ||
101 | let target = if_expr.syntax().text_range(); | 101 | let target = if_expr.syntax().text_range(); |
102 | acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { | 102 | acc.add( |
103 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); | 103 | AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite), |
104 | let new_block = match if_let_pat { | 104 | "Convert to guarded return", |
105 | None => { | 105 | target, |
106 | // If. | 106 | |edit| { |
107 | let new_expr = { | 107 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); |
108 | let then_branch = | 108 | let new_block = match if_let_pat { |
109 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 109 | None => { |
110 | let cond = invert_boolean_expression(cond_expr); | 110 | // If. |
111 | make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) | 111 | let new_expr = { |
112 | }; | 112 | let then_branch = |
113 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 113 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
114 | } | 114 | let cond = invert_boolean_expression(cond_expr); |
115 | Some((path, bound_ident)) => { | 115 | make::expr_if(make::condition(cond, None), then_branch) |
116 | // If-let. | 116 | .indent(if_indent_level) |
117 | let match_expr = { | ||
118 | let happy_arm = { | ||
119 | let pat = make::tuple_struct_pat( | ||
120 | path, | ||
121 | once(make::bind_pat(make::name("it")).into()), | ||
122 | ); | ||
123 | let expr = { | ||
124 | let name_ref = make::name_ref("it"); | ||
125 | let segment = make::path_segment(name_ref); | ||
126 | let path = make::path_unqualified(segment); | ||
127 | make::expr_path(path) | ||
128 | }; | ||
129 | make::match_arm(once(pat.into()), expr) | ||
130 | }; | 117 | }; |
118 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | ||
119 | } | ||
120 | Some((path, bound_ident)) => { | ||
121 | // If-let. | ||
122 | let match_expr = { | ||
123 | let happy_arm = { | ||
124 | let pat = make::tuple_struct_pat( | ||
125 | path, | ||
126 | once(make::bind_pat(make::name("it")).into()), | ||
127 | ); | ||
128 | let expr = { | ||
129 | let name_ref = make::name_ref("it"); | ||
130 | let segment = make::path_segment(name_ref); | ||
131 | let path = make::path_unqualified(segment); | ||
132 | make::expr_path(path) | ||
133 | }; | ||
134 | make::match_arm(once(pat.into()), expr) | ||
135 | }; | ||
131 | 136 | ||
132 | let sad_arm = make::match_arm( | 137 | let sad_arm = make::match_arm( |
133 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate | 138 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate |
134 | once(make::placeholder_pat().into()), | 139 | once(make::placeholder_pat().into()), |
135 | early_expression, | 140 | early_expression, |
136 | ); | 141 | ); |
137 | 142 | ||
138 | make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) | 143 | make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) |
139 | }; | 144 | }; |
140 | 145 | ||
141 | let let_stmt = make::let_stmt( | 146 | let let_stmt = make::let_stmt( |
142 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 147 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), |
143 | Some(match_expr), | 148 | Some(match_expr), |
149 | ); | ||
150 | let let_stmt = let_stmt.indent(if_indent_level); | ||
151 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | ||
152 | } | ||
153 | }; | ||
154 | edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); | ||
155 | |||
156 | fn replace( | ||
157 | new_expr: &SyntaxNode, | ||
158 | then_block: &ast::BlockExpr, | ||
159 | parent_block: &ast::BlockExpr, | ||
160 | if_expr: &ast::IfExpr, | ||
161 | ) -> SyntaxNode { | ||
162 | let then_block_items = then_block.dedent(IndentLevel(1)); | ||
163 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | ||
164 | let end_of_then = | ||
165 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | ||
166 | end_of_then.prev_sibling_or_token().unwrap() | ||
167 | } else { | ||
168 | end_of_then | ||
169 | }; | ||
170 | let mut then_statements = new_expr.children_with_tokens().chain( | ||
171 | then_block_items | ||
172 | .syntax() | ||
173 | .children_with_tokens() | ||
174 | .skip(1) | ||
175 | .take_while(|i| *i != end_of_then), | ||
144 | ); | 176 | ); |
145 | let let_stmt = let_stmt.indent(if_indent_level); | 177 | replace_children( |
146 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | 178 | &parent_block.syntax(), |
179 | RangeInclusive::new( | ||
180 | if_expr.clone().syntax().clone().into(), | ||
181 | if_expr.syntax().clone().into(), | ||
182 | ), | ||
183 | &mut then_statements, | ||
184 | ) | ||
147 | } | 185 | } |
148 | }; | 186 | }, |
149 | edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); | 187 | ) |
150 | |||
151 | fn replace( | ||
152 | new_expr: &SyntaxNode, | ||
153 | then_block: &ast::BlockExpr, | ||
154 | parent_block: &ast::BlockExpr, | ||
155 | if_expr: &ast::IfExpr, | ||
156 | ) -> SyntaxNode { | ||
157 | let then_block_items = then_block.dedent(IndentLevel(1)); | ||
158 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | ||
159 | let end_of_then = | ||
160 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | ||
161 | end_of_then.prev_sibling_or_token().unwrap() | ||
162 | } else { | ||
163 | end_of_then | ||
164 | }; | ||
165 | let mut then_statements = new_expr.children_with_tokens().chain( | ||
166 | then_block_items | ||
167 | .syntax() | ||
168 | .children_with_tokens() | ||
169 | .skip(1) | ||
170 | .take_while(|i| *i != end_of_then), | ||
171 | ); | ||
172 | replace_children( | ||
173 | &parent_block.syntax(), | ||
174 | RangeInclusive::new( | ||
175 | if_expr.clone().syntax().clone().into(), | ||
176 | if_expr.syntax().clone().into(), | ||
177 | ), | ||
178 | &mut then_statements, | ||
179 | ) | ||
180 | } | ||
181 | }) | ||
182 | } | 188 | } |
183 | 189 | ||
184 | #[cfg(test)] | 190 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 43b4584b4..ccec688ca 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -10,7 +10,8 @@ use ra_syntax::{ | |||
10 | use rustc_hash::FxHashSet; | 10 | use rustc_hash::FxHashSet; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, | 13 | assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, |
14 | AssistKind, Assists, | ||
14 | }; | 15 | }; |
15 | 16 | ||
16 | // Assist: extract_struct_from_enum_variant | 17 | // Assist: extract_struct_from_enum_variant |
@@ -30,30 +31,30 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
30 | acc: &mut Assists, | 31 | acc: &mut Assists, |
31 | ctx: &AssistContext, | 32 | ctx: &AssistContext, |
32 | ) -> Option<()> { | 33 | ) -> Option<()> { |
33 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | 34 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
34 | let field_list = match variant.kind() { | 35 | let field_list = match variant.kind() { |
35 | ast::StructKind::Tuple(field_list) => field_list, | 36 | ast::StructKind::Tuple(field_list) => field_list, |
36 | _ => return None, | 37 | _ => return None, |
37 | }; | 38 | }; |
38 | let variant_name = variant.name()?.to_string(); | 39 | let variant_name = variant.name()?.to_string(); |
39 | let variant_hir = ctx.sema.to_def(&variant)?; | 40 | let variant_hir = ctx.sema.to_def(&variant)?; |
40 | if existing_struct_def(ctx.db, &variant_name, &variant_hir) { | 41 | if existing_struct_def(ctx.db(), &variant_name, &variant_hir) { |
41 | return None; | 42 | return None; |
42 | } | 43 | } |
43 | let enum_ast = variant.parent_enum(); | 44 | let enum_ast = variant.parent_enum(); |
44 | let visibility = enum_ast.visibility(); | 45 | let visibility = enum_ast.visibility(); |
45 | let enum_hir = ctx.sema.to_def(&enum_ast)?; | 46 | let enum_hir = ctx.sema.to_def(&enum_ast)?; |
46 | let variant_hir_name = variant_hir.name(ctx.db); | 47 | let variant_hir_name = variant_hir.name(ctx.db()); |
47 | let enum_module_def = ModuleDef::from(enum_hir); | 48 | let enum_module_def = ModuleDef::from(enum_hir); |
48 | let current_module = enum_hir.module(ctx.db); | 49 | let current_module = enum_hir.module(ctx.db()); |
49 | let target = variant.syntax().text_range(); | 50 | let target = variant.syntax().text_range(); |
50 | acc.add( | 51 | acc.add( |
51 | AssistId("extract_struct_from_enum_variant"), | 52 | AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite), |
52 | "Extract struct from enum variant", | 53 | "Extract struct from enum variant", |
53 | target, | 54 | target, |
54 | |builder| { | 55 | |builder| { |
55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | 56 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); |
56 | let res = definition.find_usages(&ctx.db, None); | 57 | let res = definition.find_usages(&ctx.sema, None); |
57 | let start_offset = variant.parent_enum().syntax().text_range().start(); | 58 | let start_offset = variant.parent_enum().syntax().text_range().start(); |
58 | let mut visited_modules_set = FxHashSet::default(); | 59 | let mut visited_modules_set = FxHashSet::default(); |
59 | visited_modules_set.insert(current_module); | 60 | visited_modules_set.insert(current_module); |
@@ -101,7 +102,7 @@ fn insert_import( | |||
101 | enum_module_def: &ModuleDef, | 102 | enum_module_def: &ModuleDef, |
102 | variant_hir_name: &Name, | 103 | variant_hir_name: &Name, |
103 | ) -> Option<()> { | 104 | ) -> Option<()> { |
104 | let db = ctx.db; | 105 | let db = ctx.db(); |
105 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | 106 | let mod_path = module.find_use_path(db, enum_module_def.clone()); |
106 | if let Some(mut mod_path) = mod_path { | 107 | if let Some(mut mod_path) = mod_path { |
107 | mod_path.segments.pop(); | 108 | mod_path.segments.pop(); |
diff --git a/crates/ra_assists/src/handlers/extract_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs index c4150d2bb..b925a2884 100644 --- a/crates/ra_assists/src/handlers/extract_variable.rs +++ b/crates/ra_assists/src/handlers/extract_variable.rs | |||
@@ -2,14 +2,13 @@ use ra_syntax::{ | |||
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxKind::{ | 3 | SyntaxKind::{ |
4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, | 4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, |
5 | WHITESPACE, | ||
6 | }, | 5 | }, |
7 | SyntaxNode, | 6 | SyntaxNode, |
8 | }; | 7 | }; |
9 | use stdx::format_to; | 8 | use stdx::format_to; |
10 | use test_utils::mark; | 9 | use test_utils::mark; |
11 | 10 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | 11 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
13 | 12 | ||
14 | // Assist: extract_variable | 13 | // Assist: extract_variable |
15 | // | 14 | // |
@@ -36,87 +35,84 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
36 | mark::hit!(extract_var_in_comment_is_not_applicable); | 35 | mark::hit!(extract_var_in_comment_is_not_applicable); |
37 | return None; | 36 | return None; |
38 | } | 37 | } |
39 | let expr = node.ancestors().find_map(valid_target_expr)?; | 38 | let to_extract = node.ancestors().find_map(valid_target_expr)?; |
40 | let (anchor_stmt, wrap_in_block) = anchor_stmt(expr.clone())?; | 39 | let anchor = Anchor::from(&to_extract)?; |
41 | let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?.clone(); | 40 | let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); |
42 | if indent.kind() != WHITESPACE { | 41 | let target = to_extract.syntax().text_range(); |
43 | return None; | 42 | acc.add( |
44 | } | 43 | AssistId("extract_variable", AssistKind::RefactorExtract), |
45 | let target = expr.syntax().text_range(); | 44 | "Extract into variable", |
46 | acc.add(AssistId("extract_variable"), "Extract into variable", target, move |edit| { | 45 | target, |
47 | let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { | 46 | move |edit| { |
48 | Some(field) => field.name_ref(), | 47 | let field_shorthand = |
49 | None => None, | 48 | match to_extract.syntax().parent().and_then(ast::RecordExprField::cast) { |
50 | }; | 49 | Some(field) => field.name_ref(), |
51 | 50 | None => None, | |
52 | let mut buf = String::new(); | 51 | }; |
53 | 52 | ||
54 | let var_name = match &field_shorthand { | 53 | let mut buf = String::new(); |
55 | Some(it) => it.to_string(), | 54 | |
56 | None => "var_name".to_string(), | 55 | let var_name = match &field_shorthand { |
57 | }; | 56 | Some(it) => it.to_string(), |
58 | let expr_range = match &field_shorthand { | 57 | None => "var_name".to_string(), |
59 | Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()), | 58 | }; |
60 | None => expr.syntax().text_range(), | 59 | let expr_range = match &field_shorthand { |
61 | }; | 60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), |
62 | 61 | None => to_extract.syntax().text_range(), | |
63 | if wrap_in_block { | 62 | }; |
64 | format_to!(buf, "{{ let {} = ", var_name); | 63 | |
65 | } else { | 64 | if let Anchor::WrapInBlock(_) = anchor { |
66 | format_to!(buf, "let {} = ", var_name); | 65 | format_to!(buf, "{{ let {} = ", var_name); |
67 | }; | 66 | } else { |
68 | format_to!(buf, "{}", expr.syntax()); | 67 | format_to!(buf, "let {} = ", var_name); |
69 | 68 | }; | |
70 | let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); | 69 | format_to!(buf, "{}", to_extract.syntax()); |
71 | let is_full_stmt = if let Some(expr_stmt) = &full_stmt { | 70 | |
72 | Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) | 71 | if let Anchor::Replace(stmt) = anchor { |
73 | } else { | 72 | mark::hit!(test_extract_var_expr_stmt); |
74 | false | 73 | if stmt.semicolon_token().is_none() { |
75 | }; | 74 | buf.push_str(";"); |
76 | if is_full_stmt { | 75 | } |
77 | mark::hit!(test_extract_var_expr_stmt); | 76 | match ctx.config.snippet_cap { |
78 | if full_stmt.unwrap().semicolon_token().is_none() { | 77 | Some(cap) => { |
79 | buf.push_str(";"); | 78 | let snip = buf |
79 | .replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); | ||
80 | edit.replace_snippet(cap, expr_range, snip) | ||
81 | } | ||
82 | None => edit.replace(expr_range, buf), | ||
83 | } | ||
84 | return; | ||
80 | } | 85 | } |
86 | |||
87 | buf.push_str(";"); | ||
88 | |||
89 | // We want to maintain the indent level, | ||
90 | // but we do not want to duplicate possible | ||
91 | // extra newlines in the indent block | ||
92 | let text = indent.text(); | ||
93 | if text.starts_with('\n') { | ||
94 | buf.push_str("\n"); | ||
95 | buf.push_str(text.trim_start_matches('\n')); | ||
96 | } else { | ||
97 | buf.push_str(text); | ||
98 | } | ||
99 | |||
100 | edit.replace(expr_range, var_name.clone()); | ||
101 | let offset = anchor.syntax().text_range().start(); | ||
81 | match ctx.config.snippet_cap { | 102 | match ctx.config.snippet_cap { |
82 | Some(cap) => { | 103 | Some(cap) => { |
83 | let snip = | 104 | let snip = |
84 | buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); | 105 | buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); |
85 | edit.replace_snippet(cap, expr_range, snip) | 106 | edit.insert_snippet(cap, offset, snip) |
86 | } | 107 | } |
87 | None => edit.replace(expr_range, buf), | 108 | None => edit.insert(offset, buf), |
88 | } | 109 | } |
89 | return; | ||
90 | } | ||
91 | |||
92 | buf.push_str(";"); | ||
93 | |||
94 | // We want to maintain the indent level, | ||
95 | // but we do not want to duplicate possible | ||
96 | // extra newlines in the indent block | ||
97 | let text = indent.text(); | ||
98 | if text.starts_with('\n') { | ||
99 | buf.push_str("\n"); | ||
100 | buf.push_str(text.trim_start_matches('\n')); | ||
101 | } else { | ||
102 | buf.push_str(text); | ||
103 | } | ||
104 | 110 | ||
105 | edit.replace(expr_range, var_name.clone()); | 111 | if let Anchor::WrapInBlock(_) = anchor { |
106 | let offset = anchor_stmt.text_range().start(); | 112 | edit.insert(anchor.syntax().text_range().end(), " }"); |
107 | match ctx.config.snippet_cap { | ||
108 | Some(cap) => { | ||
109 | let snip = | ||
110 | buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); | ||
111 | edit.insert_snippet(cap, offset, snip) | ||
112 | } | 113 | } |
113 | None => edit.insert(offset, buf), | 114 | }, |
114 | } | 115 | ) |
115 | |||
116 | if wrap_in_block { | ||
117 | edit.insert(anchor_stmt.text_range().end(), " }"); | ||
118 | } | ||
119 | }) | ||
120 | } | 116 | } |
121 | 117 | ||
122 | /// Check whether the node is a valid expression which can be extracted to a variable. | 118 | /// Check whether the node is a valid expression which can be extracted to a variable. |
@@ -133,32 +129,48 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> { | |||
133 | } | 129 | } |
134 | } | 130 | } |
135 | 131 | ||
136 | /// Returns the syntax node which will follow the freshly extractd var | 132 | enum Anchor { |
137 | /// and a boolean indicating whether we have to wrap it within a { } block | 133 | Before(SyntaxNode), |
138 | /// to produce correct code. | 134 | Replace(ast::ExprStmt), |
139 | /// It can be a statement, the last in a block expression or a wanna be block | 135 | WrapInBlock(SyntaxNode), |
140 | /// expression like a lambda or match arm. | 136 | } |
141 | fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | 137 | |
142 | expr.syntax().ancestors().find_map(|node| { | 138 | impl Anchor { |
143 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { | 139 | fn from(to_extract: &ast::Expr) -> Option<Anchor> { |
144 | if expr.syntax() == &node { | 140 | to_extract.syntax().ancestors().find_map(|node| { |
145 | mark::hit!(test_extract_var_last_expr); | 141 | if let Some(expr) = |
146 | return Some((node, false)); | 142 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) |
143 | { | ||
144 | if expr.syntax() == &node { | ||
145 | mark::hit!(test_extract_var_last_expr); | ||
146 | return Some(Anchor::Before(node)); | ||
147 | } | ||
147 | } | 148 | } |
148 | } | ||
149 | 149 | ||
150 | if let Some(parent) = node.parent() { | 150 | if let Some(parent) = node.parent() { |
151 | if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR { | 151 | if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR { |
152 | return Some((node, true)); | 152 | return Some(Anchor::WrapInBlock(node)); |
153 | } | ||
153 | } | 154 | } |
154 | } | ||
155 | 155 | ||
156 | if ast::Stmt::cast(node.clone()).is_some() { | 156 | if let Some(stmt) = ast::Stmt::cast(node.clone()) { |
157 | return Some((node, false)); | 157 | if let ast::Stmt::ExprStmt(stmt) = stmt { |
158 | } | 158 | if stmt.expr().as_ref() == Some(to_extract) { |
159 | return Some(Anchor::Replace(stmt)); | ||
160 | } | ||
161 | } | ||
162 | return Some(Anchor::Before(node)); | ||
163 | } | ||
164 | None | ||
165 | }) | ||
166 | } | ||
159 | 167 | ||
160 | None | 168 | fn syntax(&self) -> &SyntaxNode { |
161 | }) | 169 | match self { |
170 | Anchor::Before(it) | Anchor::WrapInBlock(it) => it, | ||
171 | Anchor::Replace(stmt) => stmt.syntax(), | ||
172 | } | ||
173 | } | ||
162 | } | 174 | } |
163 | 175 | ||
164 | #[cfg(test)] | 176 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 64270c86f..708e1bc6c 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -8,7 +8,7 @@ use test_utils::mark; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | utils::{render_snippet, Cursor, FamousDefs}, | 10 | utils::{render_snippet, Cursor, FamousDefs}, |
11 | AssistContext, AssistId, Assists, | 11 | AssistContext, AssistId, AssistKind, Assists, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | // Assist: fill_match_arms | 14 | // Assist: fill_match_arms |
@@ -51,11 +51,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
51 | let module = ctx.sema.scope(expr.syntax()).module()?; | 51 | let module = ctx.sema.scope(expr.syntax()).module()?; |
52 | 52 | ||
53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | 53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { |
54 | let variants = enum_def.variants(ctx.db); | 54 | let variants = enum_def.variants(ctx.db()); |
55 | 55 | ||
56 | let mut variants = variants | 56 | let mut variants = variants |
57 | .into_iter() | 57 | .into_iter() |
58 | .filter_map(|variant| build_pat(ctx.db, module, variant)) | 58 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
61 | .collect::<Vec<_>>(); | 61 | .collect::<Vec<_>>(); |
@@ -84,11 +84,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
84 | // where each tuple represents a proposed match arm. | 84 | // where each tuple represents a proposed match arm. |
85 | enum_defs | 85 | enum_defs |
86 | .into_iter() | 86 | .into_iter() |
87 | .map(|enum_def| enum_def.variants(ctx.db)) | 87 | .map(|enum_def| enum_def.variants(ctx.db())) |
88 | .multi_cartesian_product() | 88 | .multi_cartesian_product() |
89 | .map(|variants| { | 89 | .map(|variants| { |
90 | let patterns = | 90 | let patterns = |
91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); | 91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); |
92 | ast::Pat::from(make::tuple_pat(patterns)) | 92 | ast::Pat::from(make::tuple_pat(patterns)) |
93 | }) | 93 | }) |
94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
@@ -103,24 +103,37 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
103 | } | 103 | } |
104 | 104 | ||
105 | let target = match_expr.syntax().text_range(); | 105 | let target = match_expr.syntax().text_range(); |
106 | acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { | 106 | acc.add( |
107 | let new_arm_list = match_arm_list.remove_placeholder(); | 107 | AssistId("fill_match_arms", AssistKind::QuickFix), |
108 | let n_old_arms = new_arm_list.arms().count(); | 108 | "Fill match arms", |
109 | let new_arm_list = new_arm_list.append_arms(missing_arms); | 109 | target, |
110 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | 110 | |builder| { |
111 | let old_range = match_arm_list.syntax().text_range(); | 111 | let new_arm_list = match_arm_list.remove_placeholder(); |
112 | match (first_new_arm, ctx.config.snippet_cap) { | 112 | let n_old_arms = new_arm_list.arms().count(); |
113 | (Some(first_new_arm), Some(cap)) => { | 113 | let new_arm_list = new_arm_list.append_arms(missing_arms); |
114 | let snippet = render_snippet( | 114 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); |
115 | cap, | 115 | let old_range = match_arm_list.syntax().text_range(); |
116 | new_arm_list.syntax(), | 116 | match (first_new_arm, ctx.config.snippet_cap) { |
117 | Cursor::Before(first_new_arm.syntax()), | 117 | (Some(first_new_arm), Some(cap)) => { |
118 | ); | 118 | let extend_lifetime; |
119 | builder.replace_snippet(cap, old_range, snippet); | 119 | let cursor = match first_new_arm |
120 | } | 120 | .syntax() |
121 | _ => builder.replace(old_range, new_arm_list.to_string()), | 121 | .descendants() |
122 | } | 122 | .find_map(ast::PlaceholderPat::cast) |
123 | }) | 123 | { |
124 | Some(it) => { | ||
125 | extend_lifetime = it.syntax().clone(); | ||
126 | Cursor::Replace(&extend_lifetime) | ||
127 | } | ||
128 | None => Cursor::Before(first_new_arm.syntax()), | ||
129 | }; | ||
130 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | ||
131 | builder.replace_snippet(cap, old_range, snippet); | ||
132 | } | ||
133 | _ => builder.replace(old_range, new_arm_list.to_string()), | ||
134 | } | ||
135 | }, | ||
136 | ) | ||
124 | } | 137 | } |
125 | 138 | ||
126 | fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | 139 | fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { |
@@ -286,30 +299,22 @@ mod tests { | |||
286 | check_assist( | 299 | check_assist( |
287 | fill_match_arms, | 300 | fill_match_arms, |
288 | r#" | 301 | r#" |
289 | enum A { | 302 | enum A { As, Bs, Cs(Option<i32>) } |
290 | As, | 303 | fn main() { |
291 | Bs, | 304 | match A::As<|> { |
292 | Cs(Option<i32>), | 305 | A::Cs(_) | A::Bs => {} |
293 | } | 306 | } |
294 | fn main() { | 307 | } |
295 | match A::As<|> { | 308 | "#, |
296 | A::Cs(_) | A::Bs => {} | ||
297 | } | ||
298 | } | ||
299 | "#, | ||
300 | r#" | 309 | r#" |
301 | enum A { | 310 | enum A { As, Bs, Cs(Option<i32>) } |
302 | As, | 311 | fn main() { |
303 | Bs, | 312 | match A::As { |
304 | Cs(Option<i32>), | 313 | A::Cs(_) | A::Bs => {} |
305 | } | 314 | $0A::As => {} |
306 | fn main() { | 315 | } |
307 | match A::As { | 316 | } |
308 | A::Cs(_) | A::Bs => {} | 317 | "#, |
309 | $0A::As => {} | ||
310 | } | ||
311 | } | ||
312 | "#, | ||
313 | ); | 318 | ); |
314 | } | 319 | } |
315 | 320 | ||
@@ -318,47 +323,29 @@ mod tests { | |||
318 | check_assist( | 323 | check_assist( |
319 | fill_match_arms, | 324 | fill_match_arms, |
320 | r#" | 325 | r#" |
321 | enum A { | 326 | enum A { As, Bs, Cs, Ds(String), Es(B) } |
322 | As, | 327 | enum B { Xs, Ys } |
323 | Bs, | 328 | fn main() { |
324 | Cs, | 329 | match A::As<|> { |
325 | Ds(String), | 330 | A::Bs if 0 < 1 => {} |
326 | Es(B), | 331 | A::Ds(_value) => { let x = 1; } |
327 | } | 332 | A::Es(B::Xs) => (), |
328 | enum B { | 333 | } |
329 | Xs, | 334 | } |
330 | Ys, | 335 | "#, |
331 | } | ||
332 | fn main() { | ||
333 | match A::As<|> { | ||
334 | A::Bs if 0 < 1 => {} | ||
335 | A::Ds(_value) => { let x = 1; } | ||
336 | A::Es(B::Xs) => (), | ||
337 | } | ||
338 | } | ||
339 | "#, | ||
340 | r#" | 336 | r#" |
341 | enum A { | 337 | enum A { As, Bs, Cs, Ds(String), Es(B) } |
342 | As, | 338 | enum B { Xs, Ys } |
343 | Bs, | 339 | fn main() { |
344 | Cs, | 340 | match A::As { |
345 | Ds(String), | 341 | A::Bs if 0 < 1 => {} |
346 | Es(B), | 342 | A::Ds(_value) => { let x = 1; } |
347 | } | 343 | A::Es(B::Xs) => (), |
348 | enum B { | 344 | $0A::As => {} |
349 | Xs, | 345 | A::Cs => {} |
350 | Ys, | 346 | } |
351 | } | 347 | } |
352 | fn main() { | 348 | "#, |
353 | match A::As { | ||
354 | A::Bs if 0 < 1 => {} | ||
355 | A::Ds(_value) => { let x = 1; } | ||
356 | A::Es(B::Xs) => (), | ||
357 | $0A::As => {} | ||
358 | A::Cs => {} | ||
359 | } | ||
360 | } | ||
361 | "#, | ||
362 | ); | 349 | ); |
363 | } | 350 | } |
364 | 351 | ||
@@ -367,32 +354,24 @@ mod tests { | |||
367 | check_assist( | 354 | check_assist( |
368 | fill_match_arms, | 355 | fill_match_arms, |
369 | r#" | 356 | r#" |
370 | enum A { | 357 | enum A { As, Bs, Cs(Option<i32>) } |
371 | As, | 358 | fn main() { |
372 | Bs, | 359 | match A::As<|> { |
373 | Cs(Option<i32>), | 360 | A::As(_) => {} |
374 | } | 361 | a @ A::Bs(_) => {} |
375 | fn main() { | 362 | } |
376 | match A::As<|> { | 363 | } |
377 | A::As(_) => {} | 364 | "#, |
378 | a @ A::Bs(_) => {} | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | r#" | 365 | r#" |
383 | enum A { | 366 | enum A { As, Bs, Cs(Option<i32>) } |
384 | As, | 367 | fn main() { |
385 | Bs, | 368 | match A::As { |
386 | Cs(Option<i32>), | 369 | A::As(_) => {} |
387 | } | 370 | a @ A::Bs(_) => {} |
388 | fn main() { | 371 | A::Cs(${0:_}) => {} |
389 | match A::As { | 372 | } |
390 | A::As(_) => {} | 373 | } |
391 | a @ A::Bs(_) => {} | 374 | "#, |
392 | $0A::Cs(_) => {} | ||
393 | } | ||
394 | } | ||
395 | "#, | ||
396 | ); | 375 | ); |
397 | } | 376 | } |
398 | 377 | ||
@@ -401,39 +380,27 @@ mod tests { | |||
401 | check_assist( | 380 | check_assist( |
402 | fill_match_arms, | 381 | fill_match_arms, |
403 | r#" | 382 | r#" |
404 | enum A { | 383 | enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } } |
405 | As, | ||
406 | Bs, | ||
407 | Cs(String), | ||
408 | Ds(String, String), | ||
409 | Es { x: usize, y: usize } | ||
410 | } | ||
411 | 384 | ||
412 | fn main() { | 385 | fn main() { |
413 | let a = A::As; | 386 | let a = A::As; |
414 | match a<|> {} | 387 | match a<|> {} |
415 | } | 388 | } |
416 | "#, | 389 | "#, |
417 | r#" | 390 | r#" |
418 | enum A { | 391 | enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } } |
419 | As, | ||
420 | Bs, | ||
421 | Cs(String), | ||
422 | Ds(String, String), | ||
423 | Es { x: usize, y: usize } | ||
424 | } | ||
425 | 392 | ||
426 | fn main() { | 393 | fn main() { |
427 | let a = A::As; | 394 | let a = A::As; |
428 | match a { | 395 | match a { |
429 | $0A::As => {} | 396 | $0A::As => {} |
430 | A::Bs => {} | 397 | A::Bs => {} |
431 | A::Cs(_) => {} | 398 | A::Cs(_) => {} |
432 | A::Ds(_, _) => {} | 399 | A::Ds(_, _) => {} |
433 | A::Es { x, y } => {} | 400 | A::Es { x, y } => {} |
434 | } | 401 | } |
435 | } | 402 | } |
436 | "#, | 403 | "#, |
437 | ); | 404 | ); |
438 | } | 405 | } |
439 | 406 | ||
@@ -773,7 +740,7 @@ fn foo(opt: Option<i32>) { | |||
773 | r#" | 740 | r#" |
774 | fn foo(opt: Option<i32>) { | 741 | fn foo(opt: Option<i32>) { |
775 | match opt { | 742 | match opt { |
776 | $0Some(_) => {} | 743 | Some(${0:_}) => {} |
777 | None => {} | 744 | None => {} |
778 | } | 745 | } |
779 | } | 746 | } |
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 19d4dac5e..1aefa79cc 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs | |||
@@ -2,7 +2,8 @@ use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; | |||
2 | use ra_db::FileId; | 2 | use ra_db::FileId; |
3 | use ra_syntax::{ast, AstNode, TextRange, TextSize}; | 3 | use ra_syntax::{ast, AstNode, TextRange, TextSize}; |
4 | 4 | ||
5 | use crate::{utils::vis_offset, AssistContext, AssistId, Assists}; | 5 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; |
6 | use ast::VisibilityOwner; | ||
6 | 7 | ||
7 | // FIXME: this really should be a fix for diagnostic, rather than an assist. | 8 | // FIXME: this really should be a fix for diagnostic, rather than an assist. |
8 | 9 | ||
@@ -41,14 +42,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
41 | }; | 42 | }; |
42 | 43 | ||
43 | let current_module = ctx.sema.scope(&path.syntax()).module()?; | 44 | let current_module = ctx.sema.scope(&path.syntax()).module()?; |
44 | let target_module = def.module(ctx.db)?; | 45 | let target_module = def.module(ctx.db())?; |
45 | 46 | ||
46 | let vis = target_module.visibility_of(ctx.db, &def)?; | 47 | let vis = target_module.visibility_of(ctx.db(), &def)?; |
47 | if vis.is_visible_from(ctx.db, current_module.into()) { | 48 | if vis.is_visible_from(ctx.db(), current_module.into()) { |
48 | return None; | 49 | return None; |
49 | }; | 50 | }; |
50 | 51 | ||
51 | let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; | 52 | let (offset, current_visibility, target, target_file, target_name) = |
53 | target_data_for_def(ctx.db(), def)?; | ||
52 | 54 | ||
53 | let missing_visibility = | 55 | let missing_visibility = |
54 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | 56 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; |
@@ -58,54 +60,78 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
58 | Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), | 60 | Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), |
59 | }; | 61 | }; |
60 | 62 | ||
61 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 63 | acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { |
62 | builder.edit_file(target_file); | 64 | builder.edit_file(target_file); |
63 | match ctx.config.snippet_cap { | 65 | match ctx.config.snippet_cap { |
64 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 66 | Some(cap) => match current_visibility { |
65 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 67 | Some(current_visibility) => builder.replace_snippet( |
68 | cap, | ||
69 | current_visibility.syntax().text_range(), | ||
70 | format!("$0{}", missing_visibility), | ||
71 | ), | ||
72 | None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | ||
73 | }, | ||
74 | None => match current_visibility { | ||
75 | Some(current_visibility) => { | ||
76 | builder.replace(current_visibility.syntax().text_range(), missing_visibility) | ||
77 | } | ||
78 | None => builder.insert(offset, format!("{} ", missing_visibility)), | ||
79 | }, | ||
66 | } | 80 | } |
67 | }) | 81 | }) |
68 | } | 82 | } |
69 | 83 | ||
70 | fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 84 | fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
71 | let record_field: ast::RecordField = ctx.find_node_at_offset()?; | 85 | let record_field: ast::RecordExprField = ctx.find_node_at_offset()?; |
72 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; | 86 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; |
73 | 87 | ||
74 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; | 88 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; |
75 | let visibility = record_field_def.visibility(ctx.db); | 89 | let visibility = record_field_def.visibility(ctx.db()); |
76 | if visibility.is_visible_from(ctx.db, current_module.into()) { | 90 | if visibility.is_visible_from(ctx.db(), current_module.into()) { |
77 | return None; | 91 | return None; |
78 | } | 92 | } |
79 | 93 | ||
80 | let parent = record_field_def.parent_def(ctx.db); | 94 | let parent = record_field_def.parent_def(ctx.db()); |
81 | let parent_name = parent.name(ctx.db); | 95 | let parent_name = parent.name(ctx.db()); |
82 | let target_module = parent.module(ctx.db); | 96 | let target_module = parent.module(ctx.db()); |
83 | 97 | ||
84 | let in_file_source = record_field_def.source(ctx.db); | 98 | let in_file_source = record_field_def.source(ctx.db()); |
85 | let (offset, target) = match in_file_source.value { | 99 | let (offset, current_visibility, target) = match in_file_source.value { |
86 | hir::FieldSource::Named(it) => { | 100 | hir::FieldSource::Named(it) => { |
87 | let s = it.syntax(); | 101 | let s = it.syntax(); |
88 | (vis_offset(s), s.text_range()) | 102 | (vis_offset(s), it.visibility(), s.text_range()) |
89 | } | 103 | } |
90 | hir::FieldSource::Pos(it) => { | 104 | hir::FieldSource::Pos(it) => { |
91 | let s = it.syntax(); | 105 | let s = it.syntax(); |
92 | (vis_offset(s), s.text_range()) | 106 | (vis_offset(s), it.visibility(), s.text_range()) |
93 | } | 107 | } |
94 | }; | 108 | }; |
95 | 109 | ||
96 | let missing_visibility = | 110 | let missing_visibility = |
97 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | 111 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; |
98 | let target_file = in_file_source.file_id.original_file(ctx.db); | 112 | let target_file = in_file_source.file_id.original_file(ctx.db()); |
99 | 113 | ||
100 | let target_name = record_field_def.name(ctx.db); | 114 | let target_name = record_field_def.name(ctx.db()); |
101 | let assist_label = | 115 | let assist_label = |
102 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | 116 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); |
103 | 117 | ||
104 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 118 | acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { |
105 | builder.edit_file(target_file); | 119 | builder.edit_file(target_file); |
106 | match ctx.config.snippet_cap { | 120 | match ctx.config.snippet_cap { |
107 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 121 | Some(cap) => match current_visibility { |
108 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 122 | Some(current_visibility) => builder.replace_snippet( |
123 | cap, | ||
124 | dbg!(current_visibility.syntax()).text_range(), | ||
125 | format!("$0{}", missing_visibility), | ||
126 | ), | ||
127 | None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | ||
128 | }, | ||
129 | None => match current_visibility { | ||
130 | Some(current_visibility) => { | ||
131 | builder.replace(current_visibility.syntax().text_range(), missing_visibility) | ||
132 | } | ||
133 | None => builder.insert(offset, format!("{} ", missing_visibility)), | ||
134 | }, | ||
109 | } | 135 | } |
110 | }) | 136 | }) |
111 | } | 137 | } |
@@ -113,24 +139,30 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> | |||
113 | fn target_data_for_def( | 139 | fn target_data_for_def( |
114 | db: &dyn HirDatabase, | 140 | db: &dyn HirDatabase, |
115 | def: hir::ModuleDef, | 141 | def: hir::ModuleDef, |
116 | ) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> { | 142 | ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId, Option<hir::Name>)> { |
117 | fn offset_target_and_file_id<S, Ast>( | 143 | fn offset_target_and_file_id<S, Ast>( |
118 | db: &dyn HirDatabase, | 144 | db: &dyn HirDatabase, |
119 | x: S, | 145 | x: S, |
120 | ) -> (TextSize, TextRange, FileId) | 146 | ) -> (TextSize, Option<ast::Visibility>, TextRange, FileId) |
121 | where | 147 | where |
122 | S: HasSource<Ast = Ast>, | 148 | S: HasSource<Ast = Ast>, |
123 | Ast: AstNode, | 149 | Ast: AstNode + ast::VisibilityOwner, |
124 | { | 150 | { |
125 | let source = x.source(db); | 151 | let source = x.source(db); |
126 | let in_file_syntax = source.syntax(); | 152 | let in_file_syntax = source.syntax(); |
127 | let file_id = in_file_syntax.file_id; | 153 | let file_id = in_file_syntax.file_id; |
128 | let syntax = in_file_syntax.value; | 154 | let syntax = in_file_syntax.value; |
129 | (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) | 155 | let current_visibility = source.value.visibility(); |
156 | ( | ||
157 | vis_offset(syntax), | ||
158 | current_visibility, | ||
159 | syntax.text_range(), | ||
160 | file_id.original_file(db.upcast()), | ||
161 | ) | ||
130 | } | 162 | } |
131 | 163 | ||
132 | let target_name; | 164 | let target_name; |
133 | let (offset, target, target_file) = match def { | 165 | let (offset, current_visibility, target, target_file) = match def { |
134 | hir::ModuleDef::Function(f) => { | 166 | hir::ModuleDef::Function(f) => { |
135 | target_name = Some(f.name(db)); | 167 | target_name = Some(f.name(db)); |
136 | offset_target_and_file_id(db, f) | 168 | offset_target_and_file_id(db, f) |
@@ -164,13 +196,13 @@ fn target_data_for_def( | |||
164 | let in_file_source = m.declaration_source(db)?; | 196 | let in_file_source = m.declaration_source(db)?; |
165 | let file_id = in_file_source.file_id.original_file(db.upcast()); | 197 | let file_id = in_file_source.file_id.original_file(db.upcast()); |
166 | let syntax = in_file_source.value.syntax(); | 198 | let syntax = in_file_source.value.syntax(); |
167 | (vis_offset(syntax), syntax.text_range(), file_id) | 199 | (vis_offset(syntax), in_file_source.value.visibility(), syntax.text_range(), file_id) |
168 | } | 200 | } |
169 | // Enum variants can't be private, we can't modify builtin types | 201 | // Enum variants can't be private, we can't modify builtin types |
170 | hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, | 202 | hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, |
171 | }; | 203 | }; |
172 | 204 | ||
173 | Some((offset, target, target_file, target_name)) | 205 | Some((offset, current_visibility, target, target_file, target_name)) |
174 | } | 206 | } |
175 | 207 | ||
176 | #[cfg(test)] | 208 | #[cfg(test)] |
@@ -523,6 +555,34 @@ struct Bar; | |||
523 | } | 555 | } |
524 | 556 | ||
525 | #[test] | 557 | #[test] |
558 | fn replaces_pub_crate_with_pub() { | ||
559 | check_assist( | ||
560 | fix_visibility, | ||
561 | r" | ||
562 | //- /main.rs crate:a deps:foo | ||
563 | foo::Bar<|> | ||
564 | //- /lib.rs crate:foo | ||
565 | pub(crate) struct Bar; | ||
566 | ", | ||
567 | r"$0pub struct Bar; | ||
568 | ", | ||
569 | ); | ||
570 | check_assist( | ||
571 | fix_visibility, | ||
572 | r" | ||
573 | //- /main.rs crate:a deps:foo | ||
574 | fn main() { | ||
575 | foo::Foo { <|>bar: () }; | ||
576 | } | ||
577 | //- /lib.rs crate:foo | ||
578 | pub struct Foo { pub(crate) bar: () } | ||
579 | ", | ||
580 | r"pub struct Foo { $0pub bar: () } | ||
581 | ", | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
526 | #[ignore] | 586 | #[ignore] |
527 | // FIXME handle reexports properly | 587 | // FIXME handle reexports properly |
528 | fn fix_visibility_of_reexport() { | 588 | fn fix_visibility_of_reexport() { |
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index 573196576..3cd532650 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; | 1 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
5 | // Assist: flip_binexpr | 5 | // Assist: flip_binexpr |
6 | // | 6 | // |
@@ -33,13 +33,18 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
33 | return None; | 33 | return None; |
34 | } | 34 | } |
35 | 35 | ||
36 | acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { | 36 | acc.add( |
37 | if let FlipAction::FlipAndReplaceOp(new_op) = action { | 37 | AssistId("flip_binexpr", AssistKind::RefactorRewrite), |
38 | edit.replace(op_range, new_op); | 38 | "Flip binary expression", |
39 | } | 39 | op_range, |
40 | edit.replace(lhs.text_range(), rhs.text()); | 40 | |edit| { |
41 | edit.replace(rhs.text_range(), lhs.text()); | 41 | if let FlipAction::FlipAndReplaceOp(new_op) = action { |
42 | }) | 42 | edit.replace(op_range, new_op); |
43 | } | ||
44 | edit.replace(lhs.text_range(), rhs.text()); | ||
45 | edit.replace(rhs.text_range(), lhs.text()); | ||
46 | }, | ||
47 | ) | ||
43 | } | 48 | } |
44 | 49 | ||
45 | enum FlipAction { | 50 | enum FlipAction { |
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index a57a1c463..55a971dc7 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; | 1 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
5 | // Assist: flip_comma | 5 | // Assist: flip_comma |
6 | // | 6 | // |
@@ -28,10 +28,15 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
28 | return None; | 28 | return None; |
29 | } | 29 | } |
30 | 30 | ||
31 | acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { | 31 | acc.add( |
32 | edit.replace(prev.text_range(), next.to_string()); | 32 | AssistId("flip_comma", AssistKind::RefactorRewrite), |
33 | edit.replace(next.text_range(), prev.to_string()); | 33 | "Flip comma", |
34 | }) | 34 | comma.text_range(), |
35 | |edit| { | ||
36 | edit.replace(prev.text_range(), next.to_string()); | ||
37 | edit.replace(next.text_range(), prev.to_string()); | ||
38 | }, | ||
39 | ) | ||
35 | } | 40 | } |
36 | 41 | ||
37 | #[cfg(test)] | 42 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 0115adc8b..1234f4d29 100644 --- a/crates/ra_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | Direction, T, | 4 | Direction, T, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
8 | 8 | ||
9 | // Assist: flip_trait_bound | 9 | // Assist: flip_trait_bound |
10 | // | 10 | // |
@@ -33,10 +33,15 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
33 | ); | 33 | ); |
34 | 34 | ||
35 | let target = plus.text_range(); | 35 | let target = plus.text_range(); |
36 | acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { | 36 | acc.add( |
37 | edit.replace(before.text_range(), after.to_string()); | 37 | AssistId("flip_trait_bound", AssistKind::RefactorRewrite), |
38 | edit.replace(after.text_range(), before.to_string()); | 38 | "Flip trait bounds", |
39 | }) | 39 | target, |
40 | |edit| { | ||
41 | edit.replace(before.text_range(), after.to_string()); | ||
42 | edit.replace(after.text_range(), before.to_string()); | ||
43 | }, | ||
44 | ) | ||
40 | } | 45 | } |
41 | 46 | ||
42 | #[cfg(test)] | 47 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/generate_derive.rs index b123b8498..90ece9fab 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/generate_derive.rs | |||
@@ -4,9 +4,9 @@ use ra_syntax::{ | |||
4 | TextSize, | 4 | TextSize, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
8 | 8 | ||
9 | // Assist: add_derive | 9 | // Assist: generate_derive |
10 | // | 10 | // |
11 | // Adds a new `#[derive()]` clause to a struct or enum. | 11 | // Adds a new `#[derive()]` clause to a struct or enum. |
12 | // | 12 | // |
@@ -24,36 +24,41 @@ use crate::{AssistContext, AssistId, Assists}; | |||
24 | // y: u32, | 24 | // y: u32, |
25 | // } | 25 | // } |
26 | // ``` | 26 | // ``` |
27 | pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 27 | pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
28 | let cap = ctx.config.snippet_cap?; | 28 | let cap = ctx.config.snippet_cap?; |
29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 29 | let nominal = ctx.find_node_at_offset::<ast::AdtDef>()?; |
30 | let node_start = derive_insertion_offset(&nominal)?; | 30 | let node_start = derive_insertion_offset(&nominal)?; |
31 | let target = nominal.syntax().text_range(); | 31 | let target = nominal.syntax().text_range(); |
32 | acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { | 32 | acc.add( |
33 | let derive_attr = nominal | 33 | AssistId("generate_derive", AssistKind::Generate), |
34 | .attrs() | 34 | "Add `#[derive]`", |
35 | .filter_map(|x| x.as_simple_call()) | 35 | target, |
36 | .filter(|(name, _arg)| name == "derive") | 36 | |builder| { |
37 | .map(|(_name, arg)| arg) | 37 | let derive_attr = nominal |
38 | .next(); | 38 | .attrs() |
39 | match derive_attr { | 39 | .filter_map(|x| x.as_simple_call()) |
40 | None => { | 40 | .filter(|(name, _arg)| name == "derive") |
41 | builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); | 41 | .map(|(_name, arg)| arg) |
42 | } | 42 | .next(); |
43 | Some(tt) => { | 43 | match derive_attr { |
44 | // Just move the cursor. | 44 | None => { |
45 | builder.insert_snippet( | 45 | builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); |
46 | cap, | 46 | } |
47 | tt.syntax().text_range().end() - TextSize::of(')'), | 47 | Some(tt) => { |
48 | "$0", | 48 | // Just move the cursor. |
49 | ) | 49 | builder.insert_snippet( |
50 | } | 50 | cap, |
51 | }; | 51 | tt.syntax().text_range().end() - TextSize::of(')'), |
52 | }) | 52 | "$0", |
53 | ) | ||
54 | } | ||
55 | }; | ||
56 | }, | ||
57 | ) | ||
53 | } | 58 | } |
54 | 59 | ||
55 | // Insert `derive` after doc comments. | 60 | // Insert `derive` after doc comments. |
56 | fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> { | 61 | fn derive_insertion_offset(nominal: &ast::AdtDef) -> Option<TextSize> { |
57 | let non_ws_child = nominal | 62 | let non_ws_child = nominal |
58 | .syntax() | 63 | .syntax() |
59 | .children_with_tokens() | 64 | .children_with_tokens() |
@@ -70,12 +75,12 @@ mod tests { | |||
70 | #[test] | 75 | #[test] |
71 | fn add_derive_new() { | 76 | fn add_derive_new() { |
72 | check_assist( | 77 | check_assist( |
73 | add_derive, | 78 | generate_derive, |
74 | "struct Foo { a: i32, <|>}", | 79 | "struct Foo { a: i32, <|>}", |
75 | "#[derive($0)]\nstruct Foo { a: i32, }", | 80 | "#[derive($0)]\nstruct Foo { a: i32, }", |
76 | ); | 81 | ); |
77 | check_assist( | 82 | check_assist( |
78 | add_derive, | 83 | generate_derive, |
79 | "struct Foo { <|> a: i32, }", | 84 | "struct Foo { <|> a: i32, }", |
80 | "#[derive($0)]\nstruct Foo { a: i32, }", | 85 | "#[derive($0)]\nstruct Foo { a: i32, }", |
81 | ); | 86 | ); |
@@ -84,7 +89,7 @@ mod tests { | |||
84 | #[test] | 89 | #[test] |
85 | fn add_derive_existing() { | 90 | fn add_derive_existing() { |
86 | check_assist( | 91 | check_assist( |
87 | add_derive, | 92 | generate_derive, |
88 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 93 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
89 | "#[derive(Clone$0)]\nstruct Foo { a: i32, }", | 94 | "#[derive(Clone$0)]\nstruct Foo { a: i32, }", |
90 | ); | 95 | ); |
@@ -93,7 +98,7 @@ mod tests { | |||
93 | #[test] | 98 | #[test] |
94 | fn add_derive_new_with_doc_comment() { | 99 | fn add_derive_new_with_doc_comment() { |
95 | check_assist( | 100 | check_assist( |
96 | add_derive, | 101 | generate_derive, |
97 | " | 102 | " |
98 | /// `Foo` is a pretty important struct. | 103 | /// `Foo` is a pretty important struct. |
99 | /// It does stuff. | 104 | /// It does stuff. |
@@ -111,7 +116,7 @@ struct Foo { a: i32, } | |||
111 | #[test] | 116 | #[test] |
112 | fn add_derive_target() { | 117 | fn add_derive_target() { |
113 | check_assist_target( | 118 | check_assist_target( |
114 | add_derive, | 119 | generate_derive, |
115 | " | 120 | " |
116 | struct SomeThingIrrelevant; | 121 | struct SomeThingIrrelevant; |
117 | /// `Foo` is a pretty important struct. | 122 | /// `Foo` is a pretty important struct. |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs index b0e56e1b5..9da23640a 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -2,9 +2,9 @@ use ra_ide_db::RootDatabase; | |||
2 | use ra_syntax::ast::{self, AstNode, NameOwner}; | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | use test_utils::mark; | 3 | use test_utils::mark; |
4 | 4 | ||
5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | 5 | use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
7 | // Assist: add_from_impl_for_enum | 7 | // Assist: generate_from_impl_for_enum |
8 | // | 8 | // |
9 | // Adds a From impl for an enum variant with one tuple field. | 9 | // Adds a From impl for an enum variant with one tuple field. |
10 | // | 10 | // |
@@ -21,8 +21,8 @@ use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | |||
21 | // } | 21 | // } |
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | 25 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
26 | let variant_name = variant.name()?; | 26 | let variant_name = variant.name()?; |
27 | let enum_name = variant.parent_enum().name()?; | 27 | let enum_name = variant.parent_enum().name()?; |
28 | let field_list = match variant.kind() { | 28 | let field_list = match variant.kind() { |
@@ -32,7 +32,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
32 | if field_list.fields().count() != 1 { | 32 | if field_list.fields().count() != 1 { |
33 | return None; | 33 | return None; |
34 | } | 34 | } |
35 | let field_type = field_list.fields().next()?.type_ref()?; | 35 | let field_type = field_list.fields().next()?.ty()?; |
36 | let path = match field_type { | 36 | let path = match field_type { |
37 | ast::TypeRef::PathType(it) => it, | 37 | ast::TypeRef::PathType(it) => it, |
38 | _ => return None, | 38 | _ => return None, |
@@ -45,8 +45,8 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
45 | 45 | ||
46 | let target = variant.syntax().text_range(); | 46 | let target = variant.syntax().text_range(); |
47 | acc.add( | 47 | acc.add( |
48 | AssistId("add_from_impl_for_enum"), | 48 | AssistId("generate_from_impl_for_enum", AssistKind::Generate), |
49 | "Add From impl for this enum variant", | 49 | "Generate `From` impl for this enum variant", |
50 | target, | 50 | target, |
51 | |edit| { | 51 | |edit| { |
52 | let start_offset = variant.parent_enum().syntax().text_range().end(); | 52 | let start_offset = variant.parent_enum().syntax().text_range().end(); |
@@ -69,7 +69,7 @@ impl From<{0}> for {1} {{ | |||
69 | 69 | ||
70 | fn existing_from_impl( | 70 | fn existing_from_impl( |
71 | sema: &'_ hir::Semantics<'_, RootDatabase>, | 71 | sema: &'_ hir::Semantics<'_, RootDatabase>, |
72 | variant: &ast::EnumVariant, | 72 | variant: &ast::Variant, |
73 | ) -> Option<()> { | 73 | ) -> Option<()> { |
74 | let variant = sema.to_def(variant)?; | 74 | let variant = sema.to_def(variant)?; |
75 | let enum_ = variant.parent_enum(sema.db); | 75 | let enum_ = variant.parent_enum(sema.db); |
@@ -97,9 +97,9 @@ mod tests { | |||
97 | use super::*; | 97 | use super::*; |
98 | 98 | ||
99 | #[test] | 99 | #[test] |
100 | fn test_add_from_impl_for_enum() { | 100 | fn test_generate_from_impl_for_enum() { |
101 | check_assist( | 101 | check_assist( |
102 | add_from_impl_for_enum, | 102 | generate_from_impl_for_enum, |
103 | "enum A { <|>One(u32) }", | 103 | "enum A { <|>One(u32) }", |
104 | r#"enum A { One(u32) } | 104 | r#"enum A { One(u32) } |
105 | 105 | ||
@@ -112,9 +112,9 @@ impl From<u32> for A { | |||
112 | } | 112 | } |
113 | 113 | ||
114 | #[test] | 114 | #[test] |
115 | fn test_add_from_impl_for_enum_complicated_path() { | 115 | fn test_generate_from_impl_for_enum_complicated_path() { |
116 | check_assist( | 116 | check_assist( |
117 | add_from_impl_for_enum, | 117 | generate_from_impl_for_enum, |
118 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, | 118 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
119 | r#"enum A { One(foo::bar::baz::Boo) } | 119 | r#"enum A { One(foo::bar::baz::Boo) } |
120 | 120 | ||
@@ -129,7 +129,7 @@ impl From<foo::bar::baz::Boo> for A { | |||
129 | fn check_not_applicable(ra_fixture: &str) { | 129 | fn check_not_applicable(ra_fixture: &str) { |
130 | let fixture = | 130 | let fixture = |
131 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | 131 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); |
132 | check_assist_not_applicable(add_from_impl_for_enum, &fixture) | 132 | check_assist_not_applicable(generate_from_impl_for_enum, &fixture) |
133 | } | 133 | } |
134 | 134 | ||
135 | #[test] | 135 | #[test] |
@@ -166,7 +166,7 @@ impl From<u32> for A { | |||
166 | #[test] | 166 | #[test] |
167 | fn test_add_from_impl_different_variant_impl_exists() { | 167 | fn test_add_from_impl_different_variant_impl_exists() { |
168 | check_assist( | 168 | check_assist( |
169 | add_from_impl_for_enum, | 169 | generate_from_impl_for_enum, |
170 | r#"enum A { <|>One(u32), Two(String), } | 170 | r#"enum A { <|>One(u32), Two(String), } |
171 | 171 | ||
172 | impl From<String> for A { | 172 | impl From<String> for A { |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/generate_function.rs index 1cfbd75aa..56510861d 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/generate_function.rs | |||
@@ -13,10 +13,10 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
13 | use crate::{ | 13 | use crate::{ |
14 | assist_config::SnippetCap, | 14 | assist_config::SnippetCap, |
15 | utils::{render_snippet, Cursor}, | 15 | utils::{render_snippet, Cursor}, |
16 | AssistContext, AssistId, Assists, | 16 | AssistContext, AssistId, AssistKind, Assists, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | // Assist: add_function | 19 | // Assist: generate_function |
20 | // | 20 | // |
21 | // Adds a stub function with a signature matching the function under the cursor. | 21 | // Adds a stub function with a signature matching the function under the cursor. |
22 | // | 22 | // |
@@ -41,7 +41,7 @@ use crate::{ | |||
41 | // } | 41 | // } |
42 | // | 42 | // |
43 | // ``` | 43 | // ``` |
44 | pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 44 | pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
45 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | 45 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; |
46 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 46 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; |
47 | let path = path_expr.path()?; | 47 | let path = path_expr.path()?; |
@@ -62,22 +62,27 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
63 | 63 | ||
64 | let target = call.syntax().text_range(); | 64 | let target = call.syntax().text_range(); |
65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { | 65 | acc.add( |
66 | let function_template = function_builder.render(); | 66 | AssistId("generate_function", AssistKind::Generate), |
67 | builder.edit_file(function_template.file); | 67 | format!("Generate `{}` function", function_builder.fn_name), |
68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); | 68 | target, |
69 | match ctx.config.snippet_cap { | 69 | |builder| { |
70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | 70 | let function_template = function_builder.render(); |
71 | None => builder.insert(function_template.insert_offset, new_fn), | 71 | builder.edit_file(function_template.file); |
72 | } | 72 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
73 | }) | 73 | match ctx.config.snippet_cap { |
74 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | ||
75 | None => builder.insert(function_template.insert_offset, new_fn), | ||
76 | } | ||
77 | }, | ||
78 | ) | ||
74 | } | 79 | } |
75 | 80 | ||
76 | struct FunctionTemplate { | 81 | struct FunctionTemplate { |
77 | insert_offset: TextSize, | 82 | insert_offset: TextSize, |
78 | placeholder_expr: ast::MacroCall, | 83 | placeholder_expr: ast::MacroCall, |
79 | leading_ws: String, | 84 | leading_ws: String, |
80 | fn_def: ast::FnDef, | 85 | fn_def: ast::Fn, |
81 | trailing_ws: String, | 86 | trailing_ws: String, |
82 | file: FileId, | 87 | file: FileId, |
83 | } | 88 | } |
@@ -99,7 +104,7 @@ impl FunctionTemplate { | |||
99 | struct FunctionBuilder { | 104 | struct FunctionBuilder { |
100 | target: GeneratedFunctionTarget, | 105 | target: GeneratedFunctionTarget, |
101 | fn_name: ast::Name, | 106 | fn_name: ast::Name, |
102 | type_params: Option<ast::TypeParamList>, | 107 | type_params: Option<ast::GenericParamList>, |
103 | params: ast::ParamList, | 108 | params: ast::ParamList, |
104 | file: FileId, | 109 | file: FileId, |
105 | needs_pub: bool, | 110 | needs_pub: bool, |
@@ -117,7 +122,7 @@ impl FunctionBuilder { | |||
117 | let mut file = ctx.frange.file_id; | 122 | let mut file = ctx.frange.file_id; |
118 | let target = match &target_module { | 123 | let target = match &target_module { |
119 | Some(target_module) => { | 124 | Some(target_module) => { |
120 | let module_source = target_module.definition_source(ctx.db); | 125 | let module_source = target_module.definition_source(ctx.db()); |
121 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; | 126 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
122 | file = in_file; | 127 | file = in_file; |
123 | target | 128 | target |
@@ -195,7 +200,7 @@ fn fn_args( | |||
195 | ctx: &AssistContext, | 200 | ctx: &AssistContext, |
196 | target_module: hir::Module, | 201 | target_module: hir::Module, |
197 | call: &ast::CallExpr, | 202 | call: &ast::CallExpr, |
198 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 203 | ) -> Option<(Option<ast::GenericParamList>, ast::ParamList)> { |
199 | let mut arg_names = Vec::new(); | 204 | let mut arg_names = Vec::new(); |
200 | let mut arg_types = Vec::new(); | 205 | let mut arg_types = Vec::new(); |
201 | for arg in call.arg_list()?.args() { | 206 | for arg in call.arg_list()?.args() { |
@@ -269,7 +274,7 @@ fn fn_arg_type( | |||
269 | return None; | 274 | return None; |
270 | } | 275 | } |
271 | 276 | ||
272 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | 277 | if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) { |
273 | Some(rendered) | 278 | Some(rendered) |
274 | } else { | 279 | } else { |
275 | None | 280 | None |
@@ -333,7 +338,7 @@ mod tests { | |||
333 | #[test] | 338 | #[test] |
334 | fn add_function_with_no_args() { | 339 | fn add_function_with_no_args() { |
335 | check_assist( | 340 | check_assist( |
336 | add_function, | 341 | generate_function, |
337 | r" | 342 | r" |
338 | fn foo() { | 343 | fn foo() { |
339 | bar<|>(); | 344 | bar<|>(); |
@@ -356,7 +361,7 @@ fn bar() { | |||
356 | // This ensures that the function is correctly generated | 361 | // This ensures that the function is correctly generated |
357 | // in the next outer mod or file | 362 | // in the next outer mod or file |
358 | check_assist( | 363 | check_assist( |
359 | add_function, | 364 | generate_function, |
360 | r" | 365 | r" |
361 | impl Foo { | 366 | impl Foo { |
362 | fn foo() { | 367 | fn foo() { |
@@ -382,7 +387,7 @@ fn bar() { | |||
382 | fn add_function_directly_after_current_block() { | 387 | fn add_function_directly_after_current_block() { |
383 | // The new fn should not be created at the end of the file or module | 388 | // The new fn should not be created at the end of the file or module |
384 | check_assist( | 389 | check_assist( |
385 | add_function, | 390 | generate_function, |
386 | r" | 391 | r" |
387 | fn foo1() { | 392 | fn foo1() { |
388 | bar<|>(); | 393 | bar<|>(); |
@@ -407,7 +412,7 @@ fn foo2() {} | |||
407 | #[test] | 412 | #[test] |
408 | fn add_function_with_no_args_in_same_module() { | 413 | fn add_function_with_no_args_in_same_module() { |
409 | check_assist( | 414 | check_assist( |
410 | add_function, | 415 | generate_function, |
411 | r" | 416 | r" |
412 | mod baz { | 417 | mod baz { |
413 | fn foo() { | 418 | fn foo() { |
@@ -432,7 +437,7 @@ mod baz { | |||
432 | #[test] | 437 | #[test] |
433 | fn add_function_with_function_call_arg() { | 438 | fn add_function_with_function_call_arg() { |
434 | check_assist( | 439 | check_assist( |
435 | add_function, | 440 | generate_function, |
436 | r" | 441 | r" |
437 | struct Baz; | 442 | struct Baz; |
438 | fn baz() -> Baz { todo!() } | 443 | fn baz() -> Baz { todo!() } |
@@ -457,7 +462,7 @@ fn bar(baz: Baz) { | |||
457 | #[test] | 462 | #[test] |
458 | fn add_function_with_method_call_arg() { | 463 | fn add_function_with_method_call_arg() { |
459 | check_assist( | 464 | check_assist( |
460 | add_function, | 465 | generate_function, |
461 | r" | 466 | r" |
462 | struct Baz; | 467 | struct Baz; |
463 | impl Baz { | 468 | impl Baz { |
@@ -490,7 +495,7 @@ fn bar(baz: Baz) { | |||
490 | #[test] | 495 | #[test] |
491 | fn add_function_with_string_literal_arg() { | 496 | fn add_function_with_string_literal_arg() { |
492 | check_assist( | 497 | check_assist( |
493 | add_function, | 498 | generate_function, |
494 | r#" | 499 | r#" |
495 | fn foo() { | 500 | fn foo() { |
496 | <|>bar("bar") | 501 | <|>bar("bar") |
@@ -511,7 +516,7 @@ fn bar(arg: &str) { | |||
511 | #[test] | 516 | #[test] |
512 | fn add_function_with_char_literal_arg() { | 517 | fn add_function_with_char_literal_arg() { |
513 | check_assist( | 518 | check_assist( |
514 | add_function, | 519 | generate_function, |
515 | r#" | 520 | r#" |
516 | fn foo() { | 521 | fn foo() { |
517 | <|>bar('x') | 522 | <|>bar('x') |
@@ -532,7 +537,7 @@ fn bar(arg: char) { | |||
532 | #[test] | 537 | #[test] |
533 | fn add_function_with_int_literal_arg() { | 538 | fn add_function_with_int_literal_arg() { |
534 | check_assist( | 539 | check_assist( |
535 | add_function, | 540 | generate_function, |
536 | r" | 541 | r" |
537 | fn foo() { | 542 | fn foo() { |
538 | <|>bar(42) | 543 | <|>bar(42) |
@@ -553,7 +558,7 @@ fn bar(arg: i32) { | |||
553 | #[test] | 558 | #[test] |
554 | fn add_function_with_cast_int_literal_arg() { | 559 | fn add_function_with_cast_int_literal_arg() { |
555 | check_assist( | 560 | check_assist( |
556 | add_function, | 561 | generate_function, |
557 | r" | 562 | r" |
558 | fn foo() { | 563 | fn foo() { |
559 | <|>bar(42 as u8) | 564 | <|>bar(42 as u8) |
@@ -576,7 +581,7 @@ fn bar(arg: u8) { | |||
576 | // Ensures that the name of the cast type isn't used | 581 | // Ensures that the name of the cast type isn't used |
577 | // in the generated function signature. | 582 | // in the generated function signature. |
578 | check_assist( | 583 | check_assist( |
579 | add_function, | 584 | generate_function, |
580 | r" | 585 | r" |
581 | fn foo() { | 586 | fn foo() { |
582 | let x = 42; | 587 | let x = 42; |
@@ -599,7 +604,7 @@ fn bar(x: u8) { | |||
599 | #[test] | 604 | #[test] |
600 | fn add_function_with_variable_arg() { | 605 | fn add_function_with_variable_arg() { |
601 | check_assist( | 606 | check_assist( |
602 | add_function, | 607 | generate_function, |
603 | r" | 608 | r" |
604 | fn foo() { | 609 | fn foo() { |
605 | let worble = (); | 610 | let worble = (); |
@@ -622,7 +627,7 @@ fn bar(worble: ()) { | |||
622 | #[test] | 627 | #[test] |
623 | fn add_function_with_impl_trait_arg() { | 628 | fn add_function_with_impl_trait_arg() { |
624 | check_assist( | 629 | check_assist( |
625 | add_function, | 630 | generate_function, |
626 | r" | 631 | r" |
627 | trait Foo {} | 632 | trait Foo {} |
628 | fn foo() -> impl Foo { | 633 | fn foo() -> impl Foo { |
@@ -651,7 +656,7 @@ fn bar(foo: impl Foo) { | |||
651 | #[test] | 656 | #[test] |
652 | fn borrowed_arg() { | 657 | fn borrowed_arg() { |
653 | check_assist( | 658 | check_assist( |
654 | add_function, | 659 | generate_function, |
655 | r" | 660 | r" |
656 | struct Baz; | 661 | struct Baz; |
657 | fn baz() -> Baz { todo!() } | 662 | fn baz() -> Baz { todo!() } |
@@ -678,7 +683,7 @@ fn bar(baz: &Baz) { | |||
678 | #[test] | 683 | #[test] |
679 | fn add_function_with_qualified_path_arg() { | 684 | fn add_function_with_qualified_path_arg() { |
680 | check_assist( | 685 | check_assist( |
681 | add_function, | 686 | generate_function, |
682 | r" | 687 | r" |
683 | mod Baz { | 688 | mod Baz { |
684 | pub struct Bof; | 689 | pub struct Bof; |
@@ -709,7 +714,7 @@ fn bar(baz: Baz::Bof) { | |||
709 | // FIXME fix printing the generics of a `Ty` to make this test pass | 714 | // FIXME fix printing the generics of a `Ty` to make this test pass |
710 | fn add_function_with_generic_arg() { | 715 | fn add_function_with_generic_arg() { |
711 | check_assist( | 716 | check_assist( |
712 | add_function, | 717 | generate_function, |
713 | r" | 718 | r" |
714 | fn foo<T>(t: T) { | 719 | fn foo<T>(t: T) { |
715 | <|>bar(t) | 720 | <|>bar(t) |
@@ -732,7 +737,7 @@ fn bar<T>(t: T) { | |||
732 | // FIXME Fix function type printing to make this test pass | 737 | // FIXME Fix function type printing to make this test pass |
733 | fn add_function_with_fn_arg() { | 738 | fn add_function_with_fn_arg() { |
734 | check_assist( | 739 | check_assist( |
735 | add_function, | 740 | generate_function, |
736 | r" | 741 | r" |
737 | struct Baz; | 742 | struct Baz; |
738 | impl Baz { | 743 | impl Baz { |
@@ -763,7 +768,7 @@ fn bar(arg: fn() -> Baz) { | |||
763 | // FIXME Fix closure type printing to make this test pass | 768 | // FIXME Fix closure type printing to make this test pass |
764 | fn add_function_with_closure_arg() { | 769 | fn add_function_with_closure_arg() { |
765 | check_assist( | 770 | check_assist( |
766 | add_function, | 771 | generate_function, |
767 | r" | 772 | r" |
768 | fn foo() { | 773 | fn foo() { |
769 | let closure = |x: i64| x - 1; | 774 | let closure = |x: i64| x - 1; |
@@ -786,7 +791,7 @@ fn bar(closure: impl Fn(i64) -> i64) { | |||
786 | #[test] | 791 | #[test] |
787 | fn unresolveable_types_default_to_unit() { | 792 | fn unresolveable_types_default_to_unit() { |
788 | check_assist( | 793 | check_assist( |
789 | add_function, | 794 | generate_function, |
790 | r" | 795 | r" |
791 | fn foo() { | 796 | fn foo() { |
792 | <|>bar(baz) | 797 | <|>bar(baz) |
@@ -807,7 +812,7 @@ fn bar(baz: ()) { | |||
807 | #[test] | 812 | #[test] |
808 | fn arg_names_dont_overlap() { | 813 | fn arg_names_dont_overlap() { |
809 | check_assist( | 814 | check_assist( |
810 | add_function, | 815 | generate_function, |
811 | r" | 816 | r" |
812 | struct Baz; | 817 | struct Baz; |
813 | fn baz() -> Baz { Baz } | 818 | fn baz() -> Baz { Baz } |
@@ -832,7 +837,7 @@ fn bar(baz_1: Baz, baz_2: Baz) { | |||
832 | #[test] | 837 | #[test] |
833 | fn arg_name_counters_start_at_1_per_name() { | 838 | fn arg_name_counters_start_at_1_per_name() { |
834 | check_assist( | 839 | check_assist( |
835 | add_function, | 840 | generate_function, |
836 | r#" | 841 | r#" |
837 | struct Baz; | 842 | struct Baz; |
838 | fn baz() -> Baz { Baz } | 843 | fn baz() -> Baz { Baz } |
@@ -857,7 +862,7 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | |||
857 | #[test] | 862 | #[test] |
858 | fn add_function_in_module() { | 863 | fn add_function_in_module() { |
859 | check_assist( | 864 | check_assist( |
860 | add_function, | 865 | generate_function, |
861 | r" | 866 | r" |
862 | mod bar {} | 867 | mod bar {} |
863 | 868 | ||
@@ -885,7 +890,7 @@ fn foo() { | |||
885 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | 890 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 |
886 | fn qualified_path_uses_correct_scope() { | 891 | fn qualified_path_uses_correct_scope() { |
887 | check_assist( | 892 | check_assist( |
888 | add_function, | 893 | generate_function, |
889 | " | 894 | " |
890 | mod foo { | 895 | mod foo { |
891 | pub struct Foo; | 896 | pub struct Foo; |
@@ -916,7 +921,7 @@ fn baz(foo: foo::Foo) { | |||
916 | #[test] | 921 | #[test] |
917 | fn add_function_in_module_containing_other_items() { | 922 | fn add_function_in_module_containing_other_items() { |
918 | check_assist( | 923 | check_assist( |
919 | add_function, | 924 | generate_function, |
920 | r" | 925 | r" |
921 | mod bar { | 926 | mod bar { |
922 | fn something_else() {} | 927 | fn something_else() {} |
@@ -945,7 +950,7 @@ fn foo() { | |||
945 | #[test] | 950 | #[test] |
946 | fn add_function_in_nested_module() { | 951 | fn add_function_in_nested_module() { |
947 | check_assist( | 952 | check_assist( |
948 | add_function, | 953 | generate_function, |
949 | r" | 954 | r" |
950 | mod bar { | 955 | mod bar { |
951 | mod baz {} | 956 | mod baz {} |
@@ -974,7 +979,7 @@ fn foo() { | |||
974 | #[test] | 979 | #[test] |
975 | fn add_function_in_another_file() { | 980 | fn add_function_in_another_file() { |
976 | check_assist( | 981 | check_assist( |
977 | add_function, | 982 | generate_function, |
978 | r" | 983 | r" |
979 | //- /main.rs | 984 | //- /main.rs |
980 | mod foo; | 985 | mod foo; |
@@ -996,7 +1001,7 @@ pub(crate) fn bar() { | |||
996 | #[test] | 1001 | #[test] |
997 | fn add_function_not_applicable_if_function_already_exists() { | 1002 | fn add_function_not_applicable_if_function_already_exists() { |
998 | check_assist_not_applicable( | 1003 | check_assist_not_applicable( |
999 | add_function, | 1004 | generate_function, |
1000 | r" | 1005 | r" |
1001 | fn foo() { | 1006 | fn foo() { |
1002 | bar<|>(); | 1007 | bar<|>(); |
@@ -1013,7 +1018,7 @@ fn bar() {} | |||
1013 | // bar is resolved, but baz isn't. | 1018 | // bar is resolved, but baz isn't. |
1014 | // The assist is only active if the cursor is on an unresolved path, | 1019 | // The assist is only active if the cursor is on an unresolved path, |
1015 | // but the assist should only be offered if the path is a function call. | 1020 | // but the assist should only be offered if the path is a function call. |
1016 | add_function, | 1021 | generate_function, |
1017 | r" | 1022 | r" |
1018 | fn foo() { | 1023 | fn foo() { |
1019 | bar(b<|>az); | 1024 | bar(b<|>az); |
@@ -1028,7 +1033,7 @@ fn bar(baz: ()) {} | |||
1028 | #[ignore] | 1033 | #[ignore] |
1029 | fn create_method_with_no_args() { | 1034 | fn create_method_with_no_args() { |
1030 | check_assist( | 1035 | check_assist( |
1031 | add_function, | 1036 | generate_function, |
1032 | r" | 1037 | r" |
1033 | struct Foo; | 1038 | struct Foo; |
1034 | impl Foo { | 1039 | impl Foo { |
diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs new file mode 100644 index 000000000..d9b87c9c0 --- /dev/null +++ b/crates/ra_assists/src/handlers/generate_impl.rs | |||
@@ -0,0 +1,109 @@ | |||
1 | use ra_syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner}; | ||
2 | use stdx::{format_to, SepBy}; | ||
3 | |||
4 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
5 | |||
6 | // Assist: generate_impl | ||
7 | // | ||
8 | // Adds a new inherent impl for a type. | ||
9 | // | ||
10 | // ``` | ||
11 | // struct Ctx<T: Clone> { | ||
12 | // data: T,<|> | ||
13 | // } | ||
14 | // ``` | ||
15 | // -> | ||
16 | // ``` | ||
17 | // struct Ctx<T: Clone> { | ||
18 | // data: T, | ||
19 | // } | ||
20 | // | ||
21 | // impl<T: Clone> Ctx<T> { | ||
22 | // $0 | ||
23 | // } | ||
24 | // ``` | ||
25 | pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
26 | let nominal = ctx.find_node_at_offset::<ast::AdtDef>()?; | ||
27 | let name = nominal.name()?; | ||
28 | let target = nominal.syntax().text_range(); | ||
29 | acc.add( | ||
30 | AssistId("generate_impl", AssistKind::Generate), | ||
31 | format!("Generate impl for `{}`", name), | ||
32 | target, | ||
33 | |edit| { | ||
34 | let type_params = nominal.generic_param_list(); | ||
35 | let start_offset = nominal.syntax().text_range().end(); | ||
36 | let mut buf = String::new(); | ||
37 | buf.push_str("\n\nimpl"); | ||
38 | if let Some(type_params) = &type_params { | ||
39 | format_to!(buf, "{}", type_params.syntax()); | ||
40 | } | ||
41 | buf.push_str(" "); | ||
42 | buf.push_str(name.text().as_str()); | ||
43 | if let Some(type_params) = type_params { | ||
44 | let lifetime_params = type_params | ||
45 | .lifetime_params() | ||
46 | .filter_map(|it| it.lifetime_token()) | ||
47 | .map(|it| it.text().clone()); | ||
48 | let type_params = type_params | ||
49 | .type_params() | ||
50 | .filter_map(|it| it.name()) | ||
51 | .map(|it| it.text().clone()); | ||
52 | |||
53 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); | ||
54 | format_to!(buf, "<{}>", generic_params) | ||
55 | } | ||
56 | match ctx.config.snippet_cap { | ||
57 | Some(cap) => { | ||
58 | buf.push_str(" {\n $0\n}"); | ||
59 | edit.insert_snippet(cap, start_offset, buf); | ||
60 | } | ||
61 | None => { | ||
62 | buf.push_str(" {\n}"); | ||
63 | edit.insert(start_offset, buf); | ||
64 | } | ||
65 | } | ||
66 | }, | ||
67 | ) | ||
68 | } | ||
69 | |||
70 | #[cfg(test)] | ||
71 | mod tests { | ||
72 | use crate::tests::{check_assist, check_assist_target}; | ||
73 | |||
74 | use super::*; | ||
75 | |||
76 | #[test] | ||
77 | fn test_add_impl() { | ||
78 | check_assist( | ||
79 | generate_impl, | ||
80 | "struct Foo {<|>}\n", | ||
81 | "struct Foo {}\n\nimpl Foo {\n $0\n}\n", | ||
82 | ); | ||
83 | check_assist( | ||
84 | generate_impl, | ||
85 | "struct Foo<T: Clone> {<|>}", | ||
86 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}", | ||
87 | ); | ||
88 | check_assist( | ||
89 | generate_impl, | ||
90 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
91 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn add_impl_target() { | ||
97 | check_assist_target( | ||
98 | generate_impl, | ||
99 | " | ||
100 | struct SomeThingIrrelevant; | ||
101 | /// Has a lifetime parameter | ||
102 | struct Foo<'a, T: Foo<'a>> {<|>} | ||
103 | struct EvenMoreIrrelevant; | ||
104 | ", | ||
105 | "/// Has a lifetime parameter | ||
106 | struct Foo<'a, T: Foo<'a>> {}", | ||
107 | ); | ||
108 | } | ||
109 | } | ||
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/generate_new.rs index 837aa8377..b84aa24b6 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/generate_new.rs | |||
@@ -1,15 +1,13 @@ | |||
1 | use hir::Adt; | 1 | use hir::Adt; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{ | 3 | ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner}, |
4 | self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, | ||
5 | }, | ||
6 | T, | 4 | T, |
7 | }; | 5 | }; |
8 | use stdx::{format_to, SepBy}; | 6 | use stdx::{format_to, SepBy}; |
9 | 7 | ||
10 | use crate::{AssistContext, AssistId, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
11 | 9 | ||
12 | // Assist: add_new | 10 | // Assist: generate_new |
13 | // | 11 | // |
14 | // Adds a new inherent impl for a type. | 12 | // Adds a new inherent impl for a type. |
15 | // | 13 | // |
@@ -29,8 +27,8 @@ use crate::{AssistContext, AssistId, Assists}; | |||
29 | // } | 27 | // } |
30 | // | 28 | // |
31 | // ``` | 29 | // ``` |
32 | pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 30 | pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
33 | let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; | 31 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; |
34 | 32 | ||
35 | // We want to only apply this to non-union structs with named fields | 33 | // We want to only apply this to non-union structs with named fields |
36 | let field_list = match strukt.kind() { | 34 | let field_list = match strukt.kind() { |
@@ -42,7 +40,7 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
42 | let impl_def = find_struct_impl(&ctx, &strukt)?; | 40 | let impl_def = find_struct_impl(&ctx, &strukt)?; |
43 | 41 | ||
44 | let target = strukt.syntax().text_range(); | 42 | let target = strukt.syntax().text_range(); |
45 | acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { | 43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
46 | let mut buf = String::with_capacity(512); | 44 | let mut buf = String::with_capacity(512); |
47 | 45 | ||
48 | if impl_def.is_some() { | 46 | if impl_def.is_some() { |
@@ -53,9 +51,7 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
53 | 51 | ||
54 | let params = field_list | 52 | let params = field_list |
55 | .fields() | 53 | .fields() |
56 | .filter_map(|f| { | 54 | .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))) |
57 | Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax())) | ||
58 | }) | ||
59 | .sep_by(", "); | 55 | .sep_by(", "); |
60 | let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); | 56 | let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); |
61 | 57 | ||
@@ -90,8 +86,8 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
90 | 86 | ||
91 | // Generates the surrounding `impl Type { <code> }` including type and lifetime | 87 | // Generates the surrounding `impl Type { <code> }` including type and lifetime |
92 | // parameters | 88 | // parameters |
93 | fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { | 89 | fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String { |
94 | let type_params = strukt.type_param_list(); | 90 | let type_params = strukt.generic_param_list(); |
95 | let mut buf = String::with_capacity(code.len()); | 91 | let mut buf = String::with_capacity(code.len()); |
96 | buf.push_str("\n\nimpl"); | 92 | buf.push_str("\n\nimpl"); |
97 | if let Some(type_params) = &type_params { | 93 | if let Some(type_params) = &type_params { |
@@ -121,15 +117,15 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { | |||
121 | // | 117 | // |
122 | // FIXME: change the new fn checking to a more semantic approach when that's more | 118 | // FIXME: change the new fn checking to a more semantic approach when that's more |
123 | // viable (e.g. we process proc macros, etc) | 119 | // viable (e.g. we process proc macros, etc) |
124 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { | 120 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::Struct) -> Option<Option<ast::Impl>> { |
125 | let db = ctx.db; | 121 | let db = ctx.db(); |
126 | let module = strukt.syntax().ancestors().find(|node| { | 122 | let module = strukt.syntax().ancestors().find(|node| { |
127 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 123 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
128 | })?; | 124 | })?; |
129 | 125 | ||
130 | let struct_def = ctx.sema.to_def(strukt)?; | 126 | let struct_def = ctx.sema.to_def(strukt)?; |
131 | 127 | ||
132 | let block = module.descendants().filter_map(ast::ImplDef::cast).find_map(|impl_blk| { | 128 | let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| { |
133 | let blk = ctx.sema.to_def(&impl_blk)?; | 129 | let blk = ctx.sema.to_def(&impl_blk)?; |
134 | 130 | ||
135 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` | 131 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` |
@@ -157,10 +153,10 @@ fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Opti | |||
157 | Some(block) | 153 | Some(block) |
158 | } | 154 | } |
159 | 155 | ||
160 | fn has_new_fn(imp: &ast::ImplDef) -> bool { | 156 | fn has_new_fn(imp: &ast::Impl) -> bool { |
161 | if let Some(il) = imp.item_list() { | 157 | if let Some(il) = imp.assoc_item_list() { |
162 | for item in il.assoc_items() { | 158 | for item in il.assoc_items() { |
163 | if let ast::AssocItem::FnDef(f) = item { | 159 | if let ast::AssocItem::Fn(f) = item { |
164 | if let Some(name) = f.name() { | 160 | if let Some(name) = f.name() { |
165 | if name.text().eq_ignore_ascii_case("new") { | 161 | if name.text().eq_ignore_ascii_case("new") { |
166 | return true; | 162 | return true; |
@@ -181,10 +177,10 @@ mod tests { | |||
181 | 177 | ||
182 | #[test] | 178 | #[test] |
183 | #[rustfmt::skip] | 179 | #[rustfmt::skip] |
184 | fn test_add_new() { | 180 | fn test_generate_new() { |
185 | // Check output of generation | 181 | // Check output of generation |
186 | check_assist( | 182 | check_assist( |
187 | add_new, | 183 | generate_new, |
188 | "struct Foo {<|>}", | 184 | "struct Foo {<|>}", |
189 | "struct Foo {} | 185 | "struct Foo {} |
190 | 186 | ||
@@ -194,7 +190,7 @@ impl Foo { | |||
194 | ", | 190 | ", |
195 | ); | 191 | ); |
196 | check_assist( | 192 | check_assist( |
197 | add_new, | 193 | generate_new, |
198 | "struct Foo<T: Clone> {<|>}", | 194 | "struct Foo<T: Clone> {<|>}", |
199 | "struct Foo<T: Clone> {} | 195 | "struct Foo<T: Clone> {} |
200 | 196 | ||
@@ -204,7 +200,7 @@ impl<T: Clone> Foo<T> { | |||
204 | ", | 200 | ", |
205 | ); | 201 | ); |
206 | check_assist( | 202 | check_assist( |
207 | add_new, | 203 | generate_new, |
208 | "struct Foo<'a, T: Foo<'a>> {<|>}", | 204 | "struct Foo<'a, T: Foo<'a>> {<|>}", |
209 | "struct Foo<'a, T: Foo<'a>> {} | 205 | "struct Foo<'a, T: Foo<'a>> {} |
210 | 206 | ||
@@ -214,7 +210,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> { | |||
214 | ", | 210 | ", |
215 | ); | 211 | ); |
216 | check_assist( | 212 | check_assist( |
217 | add_new, | 213 | generate_new, |
218 | "struct Foo { baz: String <|>}", | 214 | "struct Foo { baz: String <|>}", |
219 | "struct Foo { baz: String } | 215 | "struct Foo { baz: String } |
220 | 216 | ||
@@ -224,7 +220,7 @@ impl Foo { | |||
224 | ", | 220 | ", |
225 | ); | 221 | ); |
226 | check_assist( | 222 | check_assist( |
227 | add_new, | 223 | generate_new, |
228 | "struct Foo { baz: String, qux: Vec<i32> <|>}", | 224 | "struct Foo { baz: String, qux: Vec<i32> <|>}", |
229 | "struct Foo { baz: String, qux: Vec<i32> } | 225 | "struct Foo { baz: String, qux: Vec<i32> } |
230 | 226 | ||
@@ -236,7 +232,7 @@ impl Foo { | |||
236 | 232 | ||
237 | // Check that visibility modifiers don't get brought in for fields | 233 | // Check that visibility modifiers don't get brought in for fields |
238 | check_assist( | 234 | check_assist( |
239 | add_new, | 235 | generate_new, |
240 | "struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", | 236 | "struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", |
241 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 237 | "struct Foo { pub baz: String, pub qux: Vec<i32> } |
242 | 238 | ||
@@ -248,7 +244,7 @@ impl Foo { | |||
248 | 244 | ||
249 | // Check that it reuses existing impls | 245 | // Check that it reuses existing impls |
250 | check_assist( | 246 | check_assist( |
251 | add_new, | 247 | generate_new, |
252 | "struct Foo {<|>} | 248 | "struct Foo {<|>} |
253 | 249 | ||
254 | impl Foo {} | 250 | impl Foo {} |
@@ -261,7 +257,7 @@ impl Foo { | |||
261 | ", | 257 | ", |
262 | ); | 258 | ); |
263 | check_assist( | 259 | check_assist( |
264 | add_new, | 260 | generate_new, |
265 | "struct Foo {<|>} | 261 | "struct Foo {<|>} |
266 | 262 | ||
267 | impl Foo { | 263 | impl Foo { |
@@ -279,7 +275,7 @@ impl Foo { | |||
279 | ); | 275 | ); |
280 | 276 | ||
281 | check_assist( | 277 | check_assist( |
282 | add_new, | 278 | generate_new, |
283 | "struct Foo {<|>} | 279 | "struct Foo {<|>} |
284 | 280 | ||
285 | impl Foo { | 281 | impl Foo { |
@@ -304,7 +300,7 @@ impl Foo { | |||
304 | 300 | ||
305 | // Check visibility of new fn based on struct | 301 | // Check visibility of new fn based on struct |
306 | check_assist( | 302 | check_assist( |
307 | add_new, | 303 | generate_new, |
308 | "pub struct Foo {<|>}", | 304 | "pub struct Foo {<|>}", |
309 | "pub struct Foo {} | 305 | "pub struct Foo {} |
310 | 306 | ||
@@ -314,7 +310,7 @@ impl Foo { | |||
314 | ", | 310 | ", |
315 | ); | 311 | ); |
316 | check_assist( | 312 | check_assist( |
317 | add_new, | 313 | generate_new, |
318 | "pub(crate) struct Foo {<|>}", | 314 | "pub(crate) struct Foo {<|>}", |
319 | "pub(crate) struct Foo {} | 315 | "pub(crate) struct Foo {} |
320 | 316 | ||
@@ -326,9 +322,9 @@ impl Foo { | |||
326 | } | 322 | } |
327 | 323 | ||
328 | #[test] | 324 | #[test] |
329 | fn add_new_not_applicable_if_fn_exists() { | 325 | fn generate_new_not_applicable_if_fn_exists() { |
330 | check_assist_not_applicable( | 326 | check_assist_not_applicable( |
331 | add_new, | 327 | generate_new, |
332 | " | 328 | " |
333 | struct Foo {<|>} | 329 | struct Foo {<|>} |