diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 1925db8b275..edd8255f44a 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -1,5 +1,7 @@ //! See `AssistContext` +use std::mem; + use algo::find_covering_element; use hir::Semantics; use ra_db::{FileId, FileRange}; @@ -19,7 +21,6 @@ assist_config::{AssistConfig, SnippetCap}, Assist, AssistId, GroupLabel, ResolvedAssist, }; -use rustc_hash::FxHashMap; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -139,16 +140,6 @@ pub(crate) fn add( let label = Assist::new(id, label.into(), None, target); self.add_impl(label, f) } - pub(crate) fn add_in_multiple_files( - &mut self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut AssistDirector), - ) -> Option<()> { - let label = Assist::new(id, label.into(), None, target); - self.add_impl_multiple_files(label, f) - } pub(crate) fn add_group( &mut self, group: &GroupLabel, @@ -173,31 +164,6 @@ fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Opt Some(()) } - fn add_impl_multiple_files( - &mut self, - label: Assist, - f: impl FnOnce(&mut AssistDirector), - ) -> Option<()> { - if !self.resolve { - self.buf.push((label, None)); - return None; - } - let mut director = AssistDirector::default(); - f(&mut director); - let changes = director.finish(); - let file_edits: Vec = - changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect(); - - let source_change = SourceChange { - source_file_edits: file_edits, - file_system_edits: vec![], - is_snippet: false, - }; - - self.buf.push((label, Some(source_change))); - Some(()) - } - fn finish(mut self) -> Vec<(Assist, Option)> { self.buf.sort_by_key(|(label, _edit)| label.target.len()); self.buf @@ -206,13 +172,32 @@ fn finish(mut self) -> Vec<(Assist, Option)> { pub(crate) struct AssistBuilder { edit: TextEditBuilder, - file: FileId, + file_id: FileId, is_snippet: bool, + edits: Vec, } impl AssistBuilder { - pub(crate) fn new(file: FileId) -> AssistBuilder { - AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } + pub(crate) fn new(file_id: FileId) -> AssistBuilder { + AssistBuilder { + edit: TextEditBuilder::default(), + file_id, + is_snippet: false, + edits: Vec::new(), + } + } + + pub(crate) fn edit_file(&mut self, file_id: FileId) { + self.file_id = file_id; + } + + fn commit(&mut self) { + let edit = mem::take(&mut self.edit).finish(); + if !edit.is_empty() { + let new_edit = SourceFileEdit { file_id: self.file_id, edit }; + assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id)); + self.edits.push(new_edit); + } } /// Remove specified `range` of text. @@ -270,48 +255,18 @@ pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { algo::diff(&node, &new).into_text_edit(&mut self.edit) } - // FIXME: better API - pub(crate) fn set_file(&mut self, assist_file: FileId) { - self.file = assist_file; - } - // FIXME: kill this API /// Get access to the raw `TextEditBuilder`. pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { &mut self.edit } - fn finish(self) -> SourceChange { - let edit = self.edit.finish(); - let source_file_edit = SourceFileEdit { file_id: self.file, edit }; - let mut res: SourceChange = source_file_edit.into(); + fn finish(mut self) -> SourceChange { + self.commit(); + let mut res: SourceChange = mem::take(&mut self.edits).into(); if self.is_snippet { res.is_snippet = true; } res } } - -pub(crate) struct AssistDirector { - builders: FxHashMap, -} - -impl AssistDirector { - pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { - let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id)); - f(&mut builder); - } - - fn finish(self) -> Vec { - self.builders - .into_iter() - .map(|(_, builder)| builder.finish()) - .collect::>() - } -} - -impl Default for AssistDirector { - fn default() -> Self { - AssistDirector { builders: FxHashMap::default() } - } -} diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 24f931a85e1..1cfbd75aa11 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<()> let target = call.syntax().text_range(); acc.add(AssistId("add_function"), "Add function", target, |builder| { let function_template = function_builder.render(); - builder.set_file(function_template.file); + builder.edit_file(function_template.file); let new_fn = function_template.to_string(ctx.config.snippet_cap); match ctx.config.snippet_cap { 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 2c455a1fd36..44db7917a60 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,21 +1,18 @@ -use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; -use ra_syntax::{ - algo::find_node_at_offset, - ast::{self, AstNode, NameOwner}, - SourceFile, SyntaxNode, TextRange, TextSize, -}; - -use crate::{ - assist_context::{AssistBuilder, AssistDirector}, - utils::insert_use_statement, - AssistContext, AssistId, Assists, -}; -use ast::{ArgListOwner, VisibilityOwner}; use hir::{EnumVariant, Module, ModuleDef, Name}; use ra_db::FileId; use ra_fmt::leading_indent; +use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; +use ra_syntax::{ + algo::find_node_at_offset, + ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, + SourceFile, SyntaxNode, TextRange, TextSize, +}; use rustc_hash::FxHashSet; +use crate::{ + assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, +}; + // Assist: extract_struct_from_enum_variant // // Extracts a struct from enum variant. @@ -50,11 +47,11 @@ pub(crate) fn extract_struct_from_enum_variant( let enum_module_def = ModuleDef::from(enum_hir); let current_module = enum_hir.module(ctx.db); let target = variant.syntax().text_range(); - acc.add_in_multiple_files( + acc.add( AssistId("extract_struct_from_enum_variant"), "Extract struct from enum variant", target, - |edit| { + |builder| { let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); let res = definition.find_usages(&ctx.db, None); let start_offset = variant.parent_enum().syntax().text_range().start(); @@ -64,7 +61,7 @@ pub(crate) fn extract_struct_from_enum_variant( let source_file = ctx.sema.parse(reference.file_range.file_id); update_reference( ctx, - edit, + builder, reference, &source_file, &enum_module_def, @@ -73,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( ); } extract_struct_def( - edit, + builder, enum_ast.syntax(), &variant_name, &field_list.to_string(), @@ -82,7 +79,7 @@ pub(crate) fn extract_struct_from_enum_variant( &visibility, ); let list_range = field_list.syntax().text_range(); - update_variant(edit, &variant_name, ctx.frange.file_id, list_range); + update_variant(builder, &variant_name, ctx.frange.file_id, list_range); }, ) } @@ -115,7 +112,7 @@ fn insert_import( } fn extract_struct_def( - edit: &mut AssistDirector, + builder: &mut AssistBuilder, enum_ast: &SyntaxNode, variant_name: &str, variant_list: &str, @@ -142,14 +139,13 @@ fn extract_struct_def( list_with_visibility(variant_list), indent ); - edit.perform(file_id, |builder| { - builder.insert(start_offset, struct_def); - }); + builder.edit_file(file_id); + builder.insert(start_offset, struct_def); Some(()) } fn update_variant( - edit: &mut AssistDirector, + builder: &mut AssistBuilder, variant_name: &str, file_id: FileId, list_range: TextRange, @@ -158,15 +154,14 @@ fn update_variant( list_range.start().checked_add(TextSize::from(1))?, list_range.end().checked_sub(TextSize::from(1))?, ); - edit.perform(file_id, |builder| { - builder.replace(inside_variant_range, variant_name); - }); + builder.edit_file(file_id); + builder.replace(inside_variant_range, variant_name); Some(()) } fn update_reference( ctx: &AssistContext, - edit: &mut AssistDirector, + builder: &mut AssistBuilder, reference: Reference, source_file: &SourceFile, enum_module_def: &ModuleDef, @@ -186,16 +181,15 @@ fn update_reference( list_range.start().checked_add(TextSize::from(1))?, list_range.end().checked_sub(TextSize::from(1))?, ); - edit.perform(reference.file_range.file_id, |builder| { - if !visited_modules_set.contains(&module) { - if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) - .is_some() - { - visited_modules_set.insert(module); - } + builder.edit_file(reference.file_range.file_id); + if !visited_modules_set.contains(&module) { + if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) + .is_some() + { + visited_modules_set.insert(module); } - builder.replace(inside_list_range, format!("{}{}", segment, list)); - }); + } + builder.replace(inside_list_range, format!("{}{}", segment, list)); Some(()) } diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 9ec42f568c5..531b3560f2e 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 }; acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { - builder.set_file(target_file); + builder.edit_file(target_file); match ctx.config.snippet_cap { Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), None => builder.insert(offset, format!("{} ", missing_visibility)), @@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { - builder.set_file(target_file); + builder.edit_file(target_file); match ctx.config.snippet_cap { Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), None => builder.insert(offset, format!("{} ", missing_visibility)),