aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/assist_context.rs99
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs2
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs58
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs4
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
3use std::mem;
4
3use algo::find_covering_element; 5use algo::find_covering_element;
4use hir::Semantics; 6use hir::Semantics;
5use ra_db::{FileId, FileRange}; 7use 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};
22use 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
207pub(crate) struct AssistBuilder { 173pub(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
213impl AssistBuilder { 180impl 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
295pub(crate) struct AssistDirector {
296 builders: FxHashMap<FileId, AssistBuilder>,
297}
298
299impl 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
313impl 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 @@
1use hir::{EnumVariant, Module, ModuleDef, Name};
2use ra_db::FileId;
3use ra_fmt::leading_indent;
1use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; 4use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
2use ra_syntax::{ 5use 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};
10use rustc_hash::FxHashSet;
7 11
8use crate::{ 12use 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};
13use ast::{ArgListOwner, VisibilityOwner};
14use hir::{EnumVariant, Module, ModuleDef, Name};
15use ra_db::FileId;
16use ra_fmt::leading_indent;
17use 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
117fn extract_struct_def( 114fn 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
151fn update_variant( 147fn 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
167fn update_reference( 162fn 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)),