diff options
4 files changed, 56 insertions, 107 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 1925db8b2..edd8255f4 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! See `AssistContext` | 1 | //! See `AssistContext` |
2 | 2 | ||
3 | use std::mem; | ||
4 | |||
3 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
4 | use hir::Semantics; | 6 | use hir::Semantics; |
5 | use ra_db::{FileId, FileRange}; | 7 | use ra_db::{FileId, FileRange}; |
@@ -19,7 +21,6 @@ use crate::{ | |||
19 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
20 | Assist, AssistId, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, GroupLabel, ResolvedAssist, |
21 | }; | 23 | }; |
22 | use rustc_hash::FxHashMap; | ||
23 | 24 | ||
24 | /// `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. |
25 | /// | 26 | /// |
@@ -139,16 +140,6 @@ impl Assists { | |||
139 | let label = Assist::new(id, label.into(), None, target); | 140 | let label = Assist::new(id, label.into(), None, target); |
140 | self.add_impl(label, f) | 141 | self.add_impl(label, f) |
141 | } | 142 | } |
142 | pub(crate) fn add_in_multiple_files( | ||
143 | &mut self, | ||
144 | id: AssistId, | ||
145 | label: impl Into<String>, | ||
146 | target: TextRange, | ||
147 | f: impl FnOnce(&mut AssistDirector), | ||
148 | ) -> Option<()> { | ||
149 | let label = Assist::new(id, label.into(), None, target); | ||
150 | self.add_impl_multiple_files(label, f) | ||
151 | } | ||
152 | pub(crate) fn add_group( | 143 | pub(crate) fn add_group( |
153 | &mut self, | 144 | &mut self, |
154 | group: &GroupLabel, | 145 | group: &GroupLabel, |
@@ -173,31 +164,6 @@ impl Assists { | |||
173 | Some(()) | 164 | Some(()) |
174 | } | 165 | } |
175 | 166 | ||
176 | fn add_impl_multiple_files( | ||
177 | &mut self, | ||
178 | label: Assist, | ||
179 | f: impl FnOnce(&mut AssistDirector), | ||
180 | ) -> Option<()> { | ||
181 | if !self.resolve { | ||
182 | self.buf.push((label, None)); | ||
183 | return None; | ||
184 | } | ||
185 | let mut director = AssistDirector::default(); | ||
186 | f(&mut director); | ||
187 | let changes = director.finish(); | ||
188 | let file_edits: Vec<SourceFileEdit> = | ||
189 | changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect(); | ||
190 | |||
191 | let source_change = SourceChange { | ||
192 | source_file_edits: file_edits, | ||
193 | file_system_edits: vec![], | ||
194 | is_snippet: false, | ||
195 | }; | ||
196 | |||
197 | self.buf.push((label, Some(source_change))); | ||
198 | Some(()) | ||
199 | } | ||
200 | |||
201 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { | 167 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { |
202 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | 168 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); |
203 | self.buf | 169 | self.buf |
@@ -206,13 +172,32 @@ impl Assists { | |||
206 | 172 | ||
207 | pub(crate) struct AssistBuilder { | 173 | pub(crate) struct AssistBuilder { |
208 | edit: TextEditBuilder, | 174 | edit: TextEditBuilder, |
209 | file: FileId, | 175 | file_id: FileId, |
210 | is_snippet: bool, | 176 | is_snippet: bool, |
177 | edits: Vec<SourceFileEdit>, | ||
211 | } | 178 | } |
212 | 179 | ||
213 | impl AssistBuilder { | 180 | impl AssistBuilder { |
214 | pub(crate) fn new(file: FileId) -> AssistBuilder { | 181 | pub(crate) fn new(file_id: FileId) -> AssistBuilder { |
215 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } | 182 | AssistBuilder { |
183 | edit: TextEditBuilder::default(), | ||
184 | file_id, | ||
185 | is_snippet: false, | ||
186 | edits: Vec::new(), | ||
187 | } | ||
188 | } | ||
189 | |||
190 | pub(crate) fn edit_file(&mut self, file_id: FileId) { | ||
191 | self.file_id = file_id; | ||
192 | } | ||
193 | |||
194 | fn commit(&mut self) { | ||
195 | let edit = mem::take(&mut self.edit).finish(); | ||
196 | if !edit.is_empty() { | ||
197 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; | ||
198 | assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id)); | ||
199 | self.edits.push(new_edit); | ||
200 | } | ||
216 | } | 201 | } |
217 | 202 | ||
218 | /// Remove specified `range` of text. | 203 | /// Remove specified `range` of text. |
@@ -270,48 +255,18 @@ impl AssistBuilder { | |||
270 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 255 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
271 | } | 256 | } |
272 | 257 | ||
273 | // FIXME: better API | ||
274 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
275 | self.file = assist_file; | ||
276 | } | ||
277 | |||
278 | // FIXME: kill this API | 258 | // FIXME: kill this API |
279 | /// Get access to the raw `TextEditBuilder`. | 259 | /// Get access to the raw `TextEditBuilder`. |
280 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | 260 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { |
281 | &mut self.edit | 261 | &mut self.edit |
282 | } | 262 | } |
283 | 263 | ||
284 | fn finish(self) -> SourceChange { | 264 | fn finish(mut self) -> SourceChange { |
285 | let edit = self.edit.finish(); | 265 | self.commit(); |
286 | let source_file_edit = SourceFileEdit { file_id: self.file, edit }; | 266 | let mut res: SourceChange = mem::take(&mut self.edits).into(); |
287 | let mut res: SourceChange = source_file_edit.into(); | ||
288 | if self.is_snippet { | 267 | if self.is_snippet { |
289 | res.is_snippet = true; | 268 | res.is_snippet = true; |
290 | } | 269 | } |
291 | res | 270 | res |
292 | } | 271 | } |
293 | } | 272 | } |
294 | |||
295 | pub(crate) struct AssistDirector { | ||
296 | builders: FxHashMap<FileId, AssistBuilder>, | ||
297 | } | ||
298 | |||
299 | impl AssistDirector { | ||
300 | pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { | ||
301 | let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id)); | ||
302 | f(&mut builder); | ||
303 | } | ||
304 | |||
305 | fn finish(self) -> Vec<SourceChange> { | ||
306 | self.builders | ||
307 | .into_iter() | ||
308 | .map(|(_, builder)| builder.finish()) | ||
309 | .collect::<Vec<SourceChange>>() | ||
310 | } | ||
311 | } | ||
312 | |||
313 | impl Default for AssistDirector { | ||
314 | fn default() -> Self { | ||
315 | AssistDirector { builders: FxHashMap::default() } | ||
316 | } | ||
317 | } | ||
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 24f931a85..1cfbd75aa 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -64,7 +64,7 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
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(AssistId("add_function"), "Add function", target, |builder| { |
66 | let function_template = function_builder.render(); | 66 | let function_template = function_builder.render(); |
67 | builder.set_file(function_template.file); | 67 | builder.edit_file(function_template.file); |
68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); | 68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
69 | match ctx.config.snippet_cap { | 69 | match ctx.config.snippet_cap { |
70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | 70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), |
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 2c455a1fd..44db7917a 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 | |||
@@ -1,20 +1,17 @@ | |||
1 | use hir::{EnumVariant, Module, ModuleDef, Name}; | ||
2 | use ra_db::FileId; | ||
3 | use ra_fmt::leading_indent; | ||
1 | use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; | 4 | use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; |
2 | use ra_syntax::{ | 5 | use ra_syntax::{ |
3 | algo::find_node_at_offset, | 6 | algo::find_node_at_offset, |
4 | ast::{self, AstNode, NameOwner}, | 7 | ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, |
5 | SourceFile, SyntaxNode, TextRange, TextSize, | 8 | SourceFile, SyntaxNode, TextRange, TextSize, |
6 | }; | 9 | }; |
10 | use rustc_hash::FxHashSet; | ||
7 | 11 | ||
8 | use crate::{ | 12 | use crate::{ |
9 | assist_context::{AssistBuilder, AssistDirector}, | 13 | assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, |
10 | utils::insert_use_statement, | ||
11 | AssistContext, AssistId, Assists, | ||
12 | }; | 14 | }; |
13 | use ast::{ArgListOwner, VisibilityOwner}; | ||
14 | use hir::{EnumVariant, Module, ModuleDef, Name}; | ||
15 | use ra_db::FileId; | ||
16 | use ra_fmt::leading_indent; | ||
17 | use rustc_hash::FxHashSet; | ||
18 | 15 | ||
19 | // Assist: extract_struct_from_enum_variant | 16 | // Assist: extract_struct_from_enum_variant |
20 | // | 17 | // |
@@ -50,11 +47,11 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
50 | let enum_module_def = ModuleDef::from(enum_hir); | 47 | let enum_module_def = ModuleDef::from(enum_hir); |
51 | let current_module = enum_hir.module(ctx.db); | 48 | let current_module = enum_hir.module(ctx.db); |
52 | let target = variant.syntax().text_range(); | 49 | let target = variant.syntax().text_range(); |
53 | acc.add_in_multiple_files( | 50 | acc.add( |
54 | AssistId("extract_struct_from_enum_variant"), | 51 | AssistId("extract_struct_from_enum_variant"), |
55 | "Extract struct from enum variant", | 52 | "Extract struct from enum variant", |
56 | target, | 53 | target, |
57 | |edit| { | 54 | |builder| { |
58 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | 55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); |
59 | let res = definition.find_usages(&ctx.db, None); | 56 | let res = definition.find_usages(&ctx.db, None); |
60 | let start_offset = variant.parent_enum().syntax().text_range().start(); | 57 | let start_offset = variant.parent_enum().syntax().text_range().start(); |
@@ -64,7 +61,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
64 | let source_file = ctx.sema.parse(reference.file_range.file_id); | 61 | let source_file = ctx.sema.parse(reference.file_range.file_id); |
65 | update_reference( | 62 | update_reference( |
66 | ctx, | 63 | ctx, |
67 | edit, | 64 | builder, |
68 | reference, | 65 | reference, |
69 | &source_file, | 66 | &source_file, |
70 | &enum_module_def, | 67 | &enum_module_def, |
@@ -73,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
73 | ); | 70 | ); |
74 | } | 71 | } |
75 | extract_struct_def( | 72 | extract_struct_def( |
76 | edit, | 73 | builder, |
77 | enum_ast.syntax(), | 74 | enum_ast.syntax(), |
78 | &variant_name, | 75 | &variant_name, |
79 | &field_list.to_string(), | 76 | &field_list.to_string(), |
@@ -82,7 +79,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
82 | &visibility, | 79 | &visibility, |
83 | ); | 80 | ); |
84 | let list_range = field_list.syntax().text_range(); | 81 | let list_range = field_list.syntax().text_range(); |
85 | update_variant(edit, &variant_name, ctx.frange.file_id, list_range); | 82 | update_variant(builder, &variant_name, ctx.frange.file_id, list_range); |
86 | }, | 83 | }, |
87 | ) | 84 | ) |
88 | } | 85 | } |
@@ -115,7 +112,7 @@ fn insert_import( | |||
115 | } | 112 | } |
116 | 113 | ||
117 | fn extract_struct_def( | 114 | fn extract_struct_def( |
118 | edit: &mut AssistDirector, | 115 | builder: &mut AssistBuilder, |
119 | enum_ast: &SyntaxNode, | 116 | enum_ast: &SyntaxNode, |
120 | variant_name: &str, | 117 | variant_name: &str, |
121 | variant_list: &str, | 118 | variant_list: &str, |
@@ -142,14 +139,13 @@ fn extract_struct_def( | |||
142 | list_with_visibility(variant_list), | 139 | list_with_visibility(variant_list), |
143 | indent | 140 | indent |
144 | ); | 141 | ); |
145 | edit.perform(file_id, |builder| { | 142 | builder.edit_file(file_id); |
146 | builder.insert(start_offset, struct_def); | 143 | builder.insert(start_offset, struct_def); |
147 | }); | ||
148 | Some(()) | 144 | Some(()) |
149 | } | 145 | } |
150 | 146 | ||
151 | fn update_variant( | 147 | fn update_variant( |
152 | edit: &mut AssistDirector, | 148 | builder: &mut AssistBuilder, |
153 | variant_name: &str, | 149 | variant_name: &str, |
154 | file_id: FileId, | 150 | file_id: FileId, |
155 | list_range: TextRange, | 151 | list_range: TextRange, |
@@ -158,15 +154,14 @@ fn update_variant( | |||
158 | list_range.start().checked_add(TextSize::from(1))?, | 154 | list_range.start().checked_add(TextSize::from(1))?, |
159 | list_range.end().checked_sub(TextSize::from(1))?, | 155 | list_range.end().checked_sub(TextSize::from(1))?, |
160 | ); | 156 | ); |
161 | edit.perform(file_id, |builder| { | 157 | builder.edit_file(file_id); |
162 | builder.replace(inside_variant_range, variant_name); | 158 | builder.replace(inside_variant_range, variant_name); |
163 | }); | ||
164 | Some(()) | 159 | Some(()) |
165 | } | 160 | } |
166 | 161 | ||
167 | fn update_reference( | 162 | fn update_reference( |
168 | ctx: &AssistContext, | 163 | ctx: &AssistContext, |
169 | edit: &mut AssistDirector, | 164 | builder: &mut AssistBuilder, |
170 | reference: Reference, | 165 | reference: Reference, |
171 | source_file: &SourceFile, | 166 | source_file: &SourceFile, |
172 | enum_module_def: &ModuleDef, | 167 | enum_module_def: &ModuleDef, |
@@ -186,16 +181,15 @@ fn update_reference( | |||
186 | list_range.start().checked_add(TextSize::from(1))?, | 181 | list_range.start().checked_add(TextSize::from(1))?, |
187 | list_range.end().checked_sub(TextSize::from(1))?, | 182 | list_range.end().checked_sub(TextSize::from(1))?, |
188 | ); | 183 | ); |
189 | edit.perform(reference.file_range.file_id, |builder| { | 184 | builder.edit_file(reference.file_range.file_id); |
190 | if !visited_modules_set.contains(&module) { | 185 | if !visited_modules_set.contains(&module) { |
191 | if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) | 186 | if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) |
192 | .is_some() | 187 | .is_some() |
193 | { | 188 | { |
194 | visited_modules_set.insert(module); | 189 | visited_modules_set.insert(module); |
195 | } | ||
196 | } | 190 | } |
197 | builder.replace(inside_list_range, format!("{}{}", segment, list)); | 191 | } |
198 | }); | 192 | builder.replace(inside_list_range, format!("{}{}", segment, list)); |
199 | Some(()) | 193 | Some(()) |
200 | } | 194 | } |
201 | 195 | ||
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 9ec42f568..531b3560f 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs | |||
@@ -63,7 +63,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
63 | }; | 63 | }; |
64 | 64 | ||
65 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 65 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { |
66 | builder.set_file(target_file); | 66 | builder.edit_file(target_file); |
67 | match ctx.config.snippet_cap { | 67 | match ctx.config.snippet_cap { |
68 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 68 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), |
69 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 69 | None => builder.insert(offset, format!("{} ", missing_visibility)), |
@@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> | |||
106 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | 106 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); |
107 | 107 | ||
108 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 108 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { |
109 | builder.set_file(target_file); | 109 | builder.edit_file(target_file); |
110 | match ctx.config.snippet_cap { | 110 | match ctx.config.snippet_cap { |
111 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 111 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), |
112 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 112 | None => builder.insert(offset, format!("{} ", missing_visibility)), |