From 4867968d22899395e6551f22641b3617e995140c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 6 May 2020 18:45:35 +0200 Subject: Refactor assists API to be more convenient for adding new assists It now duplicates completion API in its shape. --- crates/ra_assists/src/assist_context.rs | 234 ++++++++++++++++++ crates/ra_assists/src/assist_ctx.rs | 269 --------------------- crates/ra_assists/src/handlers/add_custom_impl.rs | 9 +- crates/ra_assists/src/handlers/add_derive.rs | 9 +- .../ra_assists/src/handlers/add_explicit_type.rs | 6 +- .../src/handlers/add_from_impl_for_enum.rs | 10 +- crates/ra_assists/src/handlers/add_function.rs | 21 +- crates/ra_assists/src/handlers/add_impl.rs | 61 +++-- .../src/handlers/add_missing_impl_members.rs | 32 +-- crates/ra_assists/src/handlers/add_new.rs | 8 +- crates/ra_assists/src/handlers/apply_demorgan.rs | 6 +- crates/ra_assists/src/handlers/auto_import.rs | 41 ++-- .../src/handlers/change_return_type_to_result.rs | 10 +- .../ra_assists/src/handlers/change_visibility.rs | 31 +-- crates/ra_assists/src/handlers/early_return.rs | 161 ++++++------ crates/ra_assists/src/handlers/fill_match_arms.rs | 6 +- crates/ra_assists/src/handlers/flip_binexpr.rs | 6 +- crates/ra_assists/src/handlers/flip_comma.rs | 6 +- crates/ra_assists/src/handlers/flip_trait_bound.rs | 6 +- .../src/handlers/inline_local_variable.rs | 31 ++- .../ra_assists/src/handlers/introduce_variable.rs | 6 +- crates/ra_assists/src/handlers/invert_if.rs | 36 +-- crates/ra_assists/src/handlers/merge_imports.rs | 13 +- crates/ra_assists/src/handlers/merge_match_arms.rs | 6 +- crates/ra_assists/src/handlers/move_bounds.rs | 57 ++--- crates/ra_assists/src/handlers/move_guard.rs | 10 +- crates/ra_assists/src/handlers/raw_string.rs | 18 +- crates/ra_assists/src/handlers/remove_dbg.rs | 6 +- crates/ra_assists/src/handlers/remove_mut.rs | 6 +- crates/ra_assists/src/handlers/reorder_fields.rs | 29 +-- .../src/handlers/replace_if_let_with_match.rs | 51 ++-- .../src/handlers/replace_let_with_if_let.rs | 12 +- .../handlers/replace_qualified_name_with_use.rs | 19 +- .../src/handlers/replace_unwrap_with_match.rs | 41 ++-- crates/ra_assists/src/handlers/split_import.rs | 6 +- crates/ra_assists/src/handlers/unwrap_block.rs | 6 +- crates/ra_assists/src/lib.rs | 42 ++-- crates/ra_assists/src/tests.rs | 21 +- crates/ra_assists/src/utils/insert_use.rs | 10 +- 39 files changed, 643 insertions(+), 715 deletions(-) create mode 100644 crates/ra_assists/src/assist_context.rs delete mode 100644 crates/ra_assists/src/assist_ctx.rs (limited to 'crates/ra_assists') diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs new file mode 100644 index 000000000..203ad1273 --- /dev/null +++ b/crates/ra_assists/src/assist_context.rs @@ -0,0 +1,234 @@ +//! See `AssistContext` + +use algo::find_covering_element; +use hir::Semantics; +use ra_db::{FileId, FileRange}; +use ra_fmt::{leading_indent, reindent}; +use ra_ide_db::{ + source_change::{SingleFileChange, SourceChange}, + RootDatabase, +}; +use ra_syntax::{ + algo::{self, find_node_at_offset, SyntaxRewriter}, + AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, + TokenAtOffset, +}; +use ra_text_edit::TextEditBuilder; + +use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; + +/// `AssistContext` allows to apply an assist or check if it could be applied. +/// +/// Assists use a somewhat over-engineered approach, given the current needs. +/// The assists workflow consists of two phases. In the first phase, a user asks +/// for the list of available assists. In the second phase, the user picks a +/// particular assist and it gets applied. +/// +/// There are two peculiarities here: +/// +/// * first, we ideally avoid computing more things then necessary to answer "is +/// assist applicable" in the first phase. +/// * second, when we are applying assist, we don't have a guarantee that there +/// weren't any changes between the point when user asked for assists and when +/// they applied a particular assist. So, when applying assist, we need to do +/// all the checks from scratch. +/// +/// To avoid repeating the same code twice for both "check" and "apply" +/// functions, we use an approach reminiscent of that of Django's function based +/// views dealing with forms. Each assist receives a runtime parameter, +/// `resolve`. It first check if an edit is applicable (potentially computing +/// info required to compute the actual edit). If it is applicable, and +/// `resolve` is `true`, it then computes the actual edit. +/// +/// So, to implement the original assists workflow, we can first apply each edit +/// with `resolve = false`, and then applying the selected edit again, with +/// `resolve = true` this time. +/// +/// Note, however, that we don't actually use such two-phase logic at the +/// moment, because the LSP API is pretty awkward in this place, and it's much +/// easier to just compute the edit eagerly :-) +pub(crate) struct AssistContext<'a> { + pub(crate) sema: Semantics<'a, RootDatabase>, + pub(super) db: &'a RootDatabase, + pub(crate) frange: FileRange, + source_file: SourceFile, +} + +impl<'a> AssistContext<'a> { + pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { + let source_file = sema.parse(frange.file_id); + let db = sema.db; + AssistContext { sema, db, frange, source_file } + } + + // NB, this ignores active selection. + pub(crate) fn offset(&self) -> TextSize { + self.frange.range.start() + } + + pub(crate) fn token_at_offset(&self) -> TokenAtOffset { + self.source_file.syntax().token_at_offset(self.offset()) + } + pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option { + self.token_at_offset().find(|it| it.kind() == kind) + } + pub(crate) fn find_node_at_offset(&self) -> Option { + find_node_at_offset(self.source_file.syntax(), self.offset()) + } + pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { + self.sema + .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) + } + pub(crate) fn covering_element(&self) -> SyntaxElement { + find_covering_element(self.source_file.syntax(), self.frange.range) + } + // FIXME: remove + pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { + find_covering_element(self.source_file.syntax(), range) + } +} + +pub(crate) struct Assists { + resolve: bool, + file: FileId, + buf: Vec<(AssistLabel, Option)>, +} + +impl Assists { + pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { + Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } + } + pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { + Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } + } + + pub(crate) fn finish_unresolved(self) -> Vec { + assert!(!self.resolve); + self.finish() + .into_iter() + .map(|(label, edit)| { + assert!(edit.is_none()); + label + }) + .collect() + } + + pub(crate) fn finish_resolved(self) -> Vec { + assert!(self.resolve); + self.finish() + .into_iter() + .map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() }) + .collect() + } + + pub(crate) fn add( + &mut self, + id: AssistId, + label: impl Into, + target: TextRange, + f: impl FnOnce(&mut AssistBuilder), + ) -> Option<()> { + let label = AssistLabel::new(id, label.into(), None, target); + self.add_impl(label, f) + } + pub(crate) fn add_group( + &mut self, + group: &GroupLabel, + id: AssistId, + label: impl Into, + target: TextRange, + f: impl FnOnce(&mut AssistBuilder), + ) -> Option<()> { + let label = AssistLabel::new(id, label.into(), Some(group.clone()), target); + self.add_impl(label, f) + } + fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { + let change_label = label.label.clone(); + let source_change = if self.resolve { + let mut builder = AssistBuilder::new(self.file); + f(&mut builder); + Some(builder.finish(change_label)) + } else { + None + }; + + self.buf.push((label, source_change)); + Some(()) + } + + fn finish(mut self) -> Vec<(AssistLabel, Option)> { + self.buf.sort_by_key(|(label, _edit)| label.target.len()); + self.buf + } +} + +pub(crate) struct AssistBuilder { + edit: TextEditBuilder, + cursor_position: Option, + file: FileId, +} + +impl AssistBuilder { + pub(crate) fn new(file: FileId) -> AssistBuilder { + AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } + } + + /// Remove specified `range` of text. + pub(crate) fn delete(&mut self, range: TextRange) { + self.edit.delete(range) + } + /// Append specified `text` at the given `offset` + pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { + self.edit.insert(offset, text.into()) + } + /// Replaces specified `range` of text with a given string. + pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { + self.edit.replace(range, replace_with.into()) + } + pub(crate) fn replace_ast(&mut self, old: N, new: N) { + algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) + } + /// Replaces specified `node` of text with a given string, reindenting the + /// string to maintain `node`'s existing indent. + // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent + pub(crate) fn replace_node_and_indent( + &mut self, + node: &SyntaxNode, + replace_with: impl Into, + ) { + let mut replace_with = replace_with.into(); + if let Some(indent) = leading_indent(node) { + replace_with = reindent(&replace_with, &indent) + } + self.replace(node.text_range(), replace_with) + } + pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { + let node = rewriter.rewrite_root().unwrap(); + let new = rewriter.rewrite(&node); + algo::diff(&node, &new).into_text_edit(&mut self.edit) + } + + /// Specify desired position of the cursor after the assist is applied. + pub(crate) fn set_cursor(&mut self, offset: TextSize) { + self.cursor_position = Some(offset) + } + // 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, change_label: String) -> SourceChange { + let edit = self.edit.finish(); + if edit.is_empty() && self.cursor_position.is_none() { + panic!("Only call `add_assist` if the assist can be applied") + } + SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } + .into_source_change(self.file) + } +} diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs deleted file mode 100644 index 871671de2..000000000 --- a/crates/ra_assists/src/assist_ctx.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! This module defines `AssistCtx` -- the API surface that is exposed to assists. -use hir::Semantics; -use ra_db::{FileId, FileRange}; -use ra_fmt::{leading_indent, reindent}; -use ra_ide_db::{ - source_change::{SingleFileChange, SourceChange}, - RootDatabase, -}; -use ra_syntax::{ - algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, - AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, - TokenAtOffset, -}; -use ra_text_edit::TextEditBuilder; - -use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; - -#[derive(Clone, Debug)] -pub(crate) struct Assist(pub(crate) Vec); - -#[derive(Clone, Debug)] -pub(crate) struct AssistInfo { - pub(crate) label: AssistLabel, - pub(crate) group_label: Option, - pub(crate) source_change: Option, -} - -impl AssistInfo { - fn new(label: AssistLabel) -> AssistInfo { - AssistInfo { label, group_label: None, source_change: None } - } - - fn resolved(self, source_change: SourceChange) -> AssistInfo { - AssistInfo { source_change: Some(source_change), ..self } - } - - fn with_group(self, group_label: GroupLabel) -> AssistInfo { - AssistInfo { group_label: Some(group_label), ..self } - } - - pub(crate) fn into_resolved(self) -> Option { - let label = self.label; - self.source_change.map(|source_change| ResolvedAssist { label, source_change }) - } -} - -/// `AssistCtx` allows to apply an assist or check if it could be applied. -/// -/// Assists use a somewhat over-engineered approach, given the current needs. The -/// assists workflow consists of two phases. In the first phase, a user asks for -/// the list of available assists. In the second phase, the user picks a -/// particular assist and it gets applied. -/// -/// There are two peculiarities here: -/// -/// * first, we ideally avoid computing more things then necessary to answer -/// "is assist applicable" in the first phase. -/// * second, when we are applying assist, we don't have a guarantee that there -/// weren't any changes between the point when user asked for assists and when -/// they applied a particular assist. So, when applying assist, we need to do -/// all the checks from scratch. -/// -/// To avoid repeating the same code twice for both "check" and "apply" -/// functions, we use an approach reminiscent of that of Django's function based -/// views dealing with forms. Each assist receives a runtime parameter, -/// `should_compute_edit`. It first check if an edit is applicable (potentially -/// computing info required to compute the actual edit). If it is applicable, -/// and `should_compute_edit` is `true`, it then computes the actual edit. -/// -/// So, to implement the original assists workflow, we can first apply each edit -/// with `should_compute_edit = false`, and then applying the selected edit -/// again, with `should_compute_edit = true` this time. -/// -/// Note, however, that we don't actually use such two-phase logic at the -/// moment, because the LSP API is pretty awkward in this place, and it's much -/// easier to just compute the edit eagerly :-) -#[derive(Clone)] -pub(crate) struct AssistCtx<'a> { - pub(crate) sema: &'a Semantics<'a, RootDatabase>, - pub(crate) db: &'a RootDatabase, - pub(crate) frange: FileRange, - source_file: SourceFile, - should_compute_edit: bool, -} - -impl<'a> AssistCtx<'a> { - pub fn new( - sema: &'a Semantics<'a, RootDatabase>, - frange: FileRange, - should_compute_edit: bool, - ) -> AssistCtx<'a> { - let source_file = sema.parse(frange.file_id); - AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } - } - - pub(crate) fn add_assist( - self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut ActionBuilder), - ) -> Option { - let label = AssistLabel::new(id, label.into(), None, target); - let change_label = label.label.clone(); - let mut info = AssistInfo::new(label); - if self.should_compute_edit { - let source_change = { - let mut edit = ActionBuilder::new(&self); - f(&mut edit); - edit.build(change_label) - }; - info = info.resolved(source_change) - }; - - Some(Assist(vec![info])) - } - - pub(crate) fn add_assist_group(self, group_name: impl Into) -> AssistGroup<'a> { - let group = GroupLabel(group_name.into()); - AssistGroup { ctx: self, group, assists: Vec::new() } - } - - pub(crate) fn token_at_offset(&self) -> TokenAtOffset { - self.source_file.syntax().token_at_offset(self.frange.range.start()) - } - - pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option { - self.token_at_offset().find(|it| it.kind() == kind) - } - - pub(crate) fn find_node_at_offset(&self) -> Option { - find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) - } - - pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { - self.sema - .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) - } - - pub(crate) fn covering_element(&self) -> SyntaxElement { - find_covering_element(self.source_file.syntax(), self.frange.range) - } - pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { - find_covering_element(self.source_file.syntax(), range) - } -} - -pub(crate) struct AssistGroup<'a> { - ctx: AssistCtx<'a>, - group: GroupLabel, - assists: Vec, -} - -impl<'a> AssistGroup<'a> { - pub(crate) fn add_assist( - &mut self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut ActionBuilder), - ) { - let label = AssistLabel::new(id, label.into(), Some(self.group.clone()), target); - let change_label = label.label.clone(); - let mut info = AssistInfo::new(label).with_group(self.group.clone()); - if self.ctx.should_compute_edit { - let source_change = { - let mut edit = ActionBuilder::new(&self.ctx); - f(&mut edit); - edit.build(change_label) - }; - info = info.resolved(source_change) - }; - - self.assists.push(info) - } - - pub(crate) fn finish(self) -> Option { - if self.assists.is_empty() { - None - } else { - Some(Assist(self.assists)) - } - } -} - -pub(crate) struct ActionBuilder<'a, 'b> { - edit: TextEditBuilder, - cursor_position: Option, - file: FileId, - ctx: &'a AssistCtx<'b>, -} - -impl<'a, 'b> ActionBuilder<'a, 'b> { - fn new(ctx: &'a AssistCtx<'b>) -> Self { - Self { - edit: TextEditBuilder::default(), - cursor_position: None, - file: ctx.frange.file_id, - ctx, - } - } - - pub(crate) fn ctx(&self) -> &AssistCtx<'b> { - &self.ctx - } - - /// Replaces specified `range` of text with a given string. - pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { - self.edit.replace(range, replace_with.into()) - } - - /// Replaces specified `node` of text with a given string, reindenting the - /// string to maintain `node`'s existing indent. - // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent - pub(crate) fn replace_node_and_indent( - &mut self, - node: &SyntaxNode, - replace_with: impl Into, - ) { - let mut replace_with = replace_with.into(); - if let Some(indent) = leading_indent(node) { - replace_with = reindent(&replace_with, &indent) - } - self.replace(node.text_range(), replace_with) - } - - /// Remove specified `range` of text. - #[allow(unused)] - pub(crate) fn delete(&mut self, range: TextRange) { - self.edit.delete(range) - } - - /// Append specified `text` at the given `offset` - pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { - self.edit.insert(offset, text.into()) - } - - /// Specify desired position of the cursor after the assist is applied. - pub(crate) fn set_cursor(&mut self, offset: TextSize) { - self.cursor_position = Some(offset) - } - - /// Get access to the raw `TextEditBuilder`. - pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { - &mut self.edit - } - - pub(crate) fn replace_ast(&mut self, old: N, new: N) { - algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) - } - pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { - let node = rewriter.rewrite_root().unwrap(); - let new = rewriter.rewrite(&node); - algo::diff(&node, &new).into_text_edit(&mut self.edit) - } - - pub(crate) fn set_file(&mut self, assist_file: FileId) { - self.file = assist_file; - } - - fn build(self, change_label: String) -> SourceChange { - let edit = self.edit.finish(); - if edit.is_empty() && self.cursor_position.is_none() { - panic!("Only call `add_assist` if the assist can be applied") - } - SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } - .into_source_change(self.file) - } -} diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 869d4dc04..795a225a4 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs @@ -6,7 +6,10 @@ use ra_syntax::{ }; use stdx::SepBy; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: add_custom_impl // @@ -25,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId}; // // } // ``` -pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { +pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let input = ctx.find_node_at_offset::()?; let attr = input.syntax().parent().and_then(ast::Attr::cast)?; @@ -49,7 +52,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); let target = attr.syntax().text_range(); - ctx.add_assist(AssistId("add_custom_impl"), label, target, |edit| { + acc.add(AssistId("add_custom_impl"), label, target, |edit| { let new_attr_input = input .syntax() .descendants_with_tokens() diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index 2a6bb1cae..fb08c19e9 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_derive // @@ -24,11 +24,11 @@ use crate::{Assist, AssistCtx, AssistId}; // y: u32, // } // ``` -pub(crate) fn add_derive(ctx: AssistCtx) -> Option { +pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let node_start = derive_insertion_offset(&nominal)?; let target = nominal.syntax().text_range(); - ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { + acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { let derive_attr = nominal .attrs() .filter_map(|x| x.as_simple_call()) @@ -57,9 +57,10 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option { #[cfg(test)] mod tests { - use super::*; use crate::tests::{check_assist, check_assist_target}; + use super::*; + #[test] fn add_derive_new() { check_assist( diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index a59ec16b2..55409e501 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextRange, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_explicit_type // @@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId}; // let x: i32 = 92; // } // ``` -pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option { +pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let stmt = ctx.find_node_at_offset::()?; let expr = stmt.initializer()?; let pat = stmt.pat()?; @@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option { let db = ctx.db; let new_type_string = ty.display_truncated(db, None).to_string(); - ctx.add_assist( + acc.add( AssistId("add_explicit_type"), format!("Insert explicit type '{}'", new_type_string), pat_range, diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 81deb3dfa..275184e24 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -4,10 +4,10 @@ use ra_syntax::{ TextSize, }; use stdx::format_to; - -use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; use test_utils::tested_by; +use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; + // Assist add_from_impl_for_enum // // Adds a From impl for an enum variant with one tuple field @@ -25,7 +25,7 @@ use test_utils::tested_by; // } // } // ``` -pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option { +pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let enum_name = variant.parent_enum().name()?; @@ -42,13 +42,13 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option { _ => return None, }; - if existing_from_impl(ctx.sema, &variant).is_some() { + if existing_from_impl(&ctx.sema, &variant).is_some() { tested_by!(test_add_from_impl_already_exists); return None; } let target = variant.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("add_from_impl_for_enum"), "Add From impl for this enum variant", target, diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 278079665..6b5616aa9 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -1,14 +1,13 @@ +use hir::HirDisplay; +use ra_db::FileId; use ra_syntax::{ - ast::{self, AstNode}, + ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, SyntaxKind, SyntaxNode, TextSize, }; - -use crate::{Assist, AssistCtx, AssistId}; -use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; -use hir::HirDisplay; -use ra_db::FileId; use rustc_hash::{FxHashMap, FxHashSet}; +use crate::{AssistContext, AssistId, Assists}; + // Assist: add_function // // Adds a stub function with a signature matching the function under the cursor. @@ -34,7 +33,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; // } // // ``` -pub(crate) fn add_function(ctx: AssistCtx) -> Option { +pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let path = path_expr.path()?; @@ -59,7 +58,7 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option { let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; let target = call.syntax().text_range(); - ctx.add_assist(AssistId("add_function"), "Add function", target, |edit| { + acc.add(AssistId("add_function"), "Add function", target, |edit| { let function_template = function_builder.render(); edit.set_file(function_template.file); edit.set_cursor(function_template.cursor_offset); @@ -87,7 +86,7 @@ impl FunctionBuilder { /// Prepares a generated function that matches `call` in `generate_in` /// (or as close to `call` as possible, if `generate_in` is `None`) fn from_call( - ctx: &AssistCtx, + ctx: &AssistContext, call: &ast::CallExpr, path: &ast::Path, target_module: Option>, @@ -152,7 +151,7 @@ fn fn_name(call: &ast::Path) -> Option { /// Computes the type variables and arguments required for the generated function fn fn_args( - ctx: &AssistCtx, + ctx: &AssistContext, call: &ast::CallExpr, ) -> Option<(Option, ast::ParamList)> { let mut arg_names = Vec::new(); @@ -219,7 +218,7 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option { } } -fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option { +fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option { let ty = ctx.sema.type_of_expr(fn_arg)?; if ty.is_unknown() { return None; diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs index 557344ebb..df114a0d8 100644 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ b/crates/ra_assists/src/handlers/add_impl.rs @@ -4,7 +4,7 @@ use ra_syntax::{ }; use stdx::{format_to, SepBy}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_impl // @@ -25,43 +25,36 @@ use crate::{Assist, AssistCtx, AssistId}; // // } // ``` -pub(crate) fn add_impl(ctx: AssistCtx) -> Option { +pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); - ctx.add_assist( - AssistId("add_impl"), - format!("Implement {}", name.text().as_str()), - target, - |edit| { - let type_params = nominal.type_param_list(); - let start_offset = nominal.syntax().text_range().end(); - let mut buf = String::new(); - buf.push_str("\n\nimpl"); - if let Some(type_params) = &type_params { - format_to!(buf, "{}", type_params.syntax()); - } - buf.push_str(" "); - buf.push_str(name.text().as_str()); - if let Some(type_params) = type_params { - let lifetime_params = type_params - .lifetime_params() - .filter_map(|it| it.lifetime_token()) - .map(|it| it.text().clone()); - let type_params = type_params - .type_params() - .filter_map(|it| it.name()) - .map(|it| it.text().clone()); + acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| { + let type_params = nominal.type_param_list(); + let start_offset = nominal.syntax().text_range().end(); + let mut buf = String::new(); + buf.push_str("\n\nimpl"); + if let Some(type_params) = &type_params { + format_to!(buf, "{}", type_params.syntax()); + } + buf.push_str(" "); + buf.push_str(name.text().as_str()); + if let Some(type_params) = type_params { + let lifetime_params = type_params + .lifetime_params() + .filter_map(|it| it.lifetime_token()) + .map(|it| it.text().clone()); + let type_params = + type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); - let generic_params = lifetime_params.chain(type_params).sep_by(", "); - format_to!(buf, "<{}>", generic_params) - } - buf.push_str(" {\n"); - edit.set_cursor(start_offset + TextSize::of(&buf)); - buf.push_str("\n}"); - edit.insert(start_offset, buf); - }, - ) + let generic_params = lifetime_params.chain(type_params).sep_by(", "); + format_to!(buf, "<{}>", generic_params) + } + buf.push_str(" {\n"); + edit.set_cursor(start_offset + TextSize::of(&buf)); + buf.push_str("\n}"); + edit.insert(start_offset, buf); + }) } #[cfg(test)] 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 7df786590..3482a75bf 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -9,9 +9,10 @@ use ra_syntax::{ }; use crate::{ + assist_context::{AssistContext, Assists}, ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, utils::{get_missing_assoc_items, resolve_target_trait}, - Assist, AssistCtx, AssistId, + AssistId, }; #[derive(PartialEq)] @@ -50,8 +51,9 @@ enum AddMissingImplMembersMode { // // } // ``` -pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { +pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { add_missing_impl_members_inner( + acc, ctx, AddMissingImplMembersMode::NoDefaultMethods, "add_impl_missing_members", @@ -91,8 +93,9 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { // // } // ``` -pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option { +pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { add_missing_impl_members_inner( + acc, ctx, AddMissingImplMembersMode::DefaultMethodsOnly, "add_impl_default_members", @@ -101,11 +104,12 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option { } fn add_missing_impl_members_inner( - ctx: AssistCtx, + acc: &mut Assists, + ctx: &AssistContext, mode: AddMissingImplMembersMode, assist_id: &'static str, label: &'static str, -) -> Option { +) -> Option<()> { let _p = ra_prof::profile("add_missing_impl_members_inner"); let impl_def = ctx.find_node_at_offset::()?; let impl_item_list = impl_def.item_list()?; @@ -142,12 +146,11 @@ fn add_missing_impl_members_inner( return None; } - let sema = ctx.sema; let target = impl_def.syntax().text_range(); - ctx.add_assist(AssistId(assist_id), label, target, |edit| { + acc.add(AssistId(assist_id), label, target, |edit| { let n_existing_items = impl_item_list.assoc_items().count(); - let source_scope = sema.scope_for_def(trait_); - let target_scope = sema.scope(impl_item_list.syntax()); + let source_scope = ctx.sema.scope_for_def(trait_); + let target_scope = ctx.sema.scope(impl_item_list.syntax()); let ast_transform = QualifyPaths::new(&target_scope, &source_scope) .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); let items = missing_items @@ -170,13 +173,12 @@ fn add_missing_impl_members_inner( } fn add_body(fn_def: ast::FnDef) -> ast::FnDef { - if fn_def.body().is_none() { - let body = make::block_expr(None, Some(make::expr_todo())); - let body = IndentLevel(1).increase_indent(body); - fn_def.with_body(body) - } else { - fn_def + if fn_def.body().is_some() { + return fn_def; } + let body = make::block_expr(None, Some(make::expr_todo())); + let body = IndentLevel(1).increase_indent(body); + fn_def.with_body(body) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 1c3f8435a..fe7451dcf 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs @@ -7,7 +7,7 @@ use ra_syntax::{ }; use stdx::{format_to, SepBy}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_new // @@ -29,7 +29,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // // ``` -pub(crate) fn add_new(ctx: AssistCtx) -> Option { +pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; // We want to only apply this to non-union structs with named fields @@ -42,7 +42,7 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option { let impl_def = find_struct_impl(&ctx, &strukt)?; let target = strukt.syntax().text_range(); - ctx.add_assist(AssistId("add_new"), "Add default constructor", target, |edit| { + acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { let mut buf = String::with_capacity(512); if impl_def.is_some() { @@ -123,7 +123,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { // // FIXME: change the new fn checking to a more semantic approach when that's more // viable (e.g. we process proc macros, etc) -fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option> { +fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option> { let db = ctx.db; let module = strukt.syntax().ancestors().find(|node| { ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index a5b26e5b9..0feba5e11 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs @@ -1,6 +1,6 @@ use ra_syntax::ast::{self, AstNode}; -use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; +use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; // Assist: apply_demorgan // @@ -21,7 +21,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; // if !(x == 4 && y) {} // } // ``` -pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option { +pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let expr = ctx.find_node_at_offset::()?; let op = expr.op_kind()?; let op_range = expr.op_token()?.text_range(); @@ -39,7 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option { let rhs_range = rhs.syntax().text_range(); let not_rhs = invert_boolean_expression(rhs); - ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { + acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { edit.replace(op_range, opposite_op); edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 2224b9714..78d23150d 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -1,5 +1,6 @@ use std::collections::BTreeSet; +use either::Either; use hir::{ AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, Type, @@ -12,12 +13,7 @@ use ra_syntax::{ }; use rustc_hash::FxHashSet; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::insert_use_statement, - AssistId, -}; -use either::Either; +use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; // Assist: auto_import // @@ -38,7 +34,7 @@ use either::Either; // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn auto_import(ctx: AssistCtx) -> Option { +pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let auto_import_assets = AutoImportAssets::new(&ctx)?; let proposed_imports = auto_import_assets.search_for_imports(ctx.db); if proposed_imports.is_empty() { @@ -46,13 +42,19 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { } let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; - let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); + let group = auto_import_assets.get_import_group_message(); for import in proposed_imports { - group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), range, |edit| { - insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); - }); + acc.add_group( + &group, + AssistId("auto_import"), + format!("Import `{}`", &import), + range, + |builder| { + insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); + }, + ); } - group.finish() + Some(()) } #[derive(Debug)] @@ -63,7 +65,7 @@ struct AutoImportAssets { } impl AutoImportAssets { - fn new(ctx: &AssistCtx) -> Option { + fn new(ctx: &AssistContext) -> Option { if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::() { Self::for_regular_path(path_under_caret, &ctx) } else { @@ -71,7 +73,7 @@ impl AutoImportAssets { } } - fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option { + fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option { let syntax_under_caret = method_call.syntax().to_owned(); let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; Some(Self { @@ -81,7 +83,7 @@ impl AutoImportAssets { }) } - fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option { + fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option { let syntax_under_caret = path_under_caret.syntax().to_owned(); if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { return None; @@ -104,8 +106,8 @@ impl AutoImportAssets { } } - fn get_import_group_message(&self) -> String { - match &self.import_candidate { + fn get_import_group_message(&self) -> GroupLabel { + let name = match &self.import_candidate { ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), ImportCandidate::QualifierStart(qualifier_start) => { format!("Import {}", qualifier_start) @@ -116,7 +118,8 @@ impl AutoImportAssets { ImportCandidate::TraitMethod(_, trait_method_name) => { format!("Import a trait for method {}", trait_method_name) } - } + }; + GroupLabel(name) } fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet { @@ -383,7 +386,7 @@ mod tests { } ", r" - use PubMod1::PubStruct; + use PubMod3::PubStruct; PubSt<|>ruct 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 1e8d986cd..5c907097e 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -1,11 +1,11 @@ use ra_syntax::{ - ast, AstNode, + ast::{self, BlockExpr, Expr, LoopBodyOwner}, + AstNode, SyntaxKind::{COMMENT, WHITESPACE}, SyntaxNode, TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; -use ast::{BlockExpr, Expr, LoopBodyOwner}; +use crate::{AssistContext, AssistId, Assists}; // Assist: change_return_type_to_result // @@ -18,7 +18,7 @@ use ast::{BlockExpr, Expr, LoopBodyOwner}; // ``` // fn foo() -> Result { Ok(42i32) } // ``` -pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option { +pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let fn_def = ctx.find_node_at_offset::(); let fn_def = &mut fn_def?; let ret_type = &fn_def.ret_type()?.type_ref()?; @@ -33,7 +33,7 @@ pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist( + acc.add( AssistId("change_return_type_to_result"), "Change return type to Result", ret_type.syntax().text_range(), diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 489db83e6..e631766ef 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -7,10 +7,10 @@ use ra_syntax::{ }, SyntaxNode, TextSize, T, }; - -use crate::{Assist, AssistCtx, AssistId}; use test_utils::tested_by; +use crate::{AssistContext, AssistId, Assists}; + // Assist: change_visibility // // Adds or changes existing visibility specifier. @@ -22,14 +22,14 @@ use test_utils::tested_by; // ``` // pub(crate) fn frobnicate() {} // ``` -pub(crate) fn change_visibility(ctx: AssistCtx) -> Option { +pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { if let Some(vis) = ctx.find_node_at_offset::() { - return change_vis(ctx, vis); + return change_vis(acc, vis); } - add_vis(ctx) + add_vis(acc, ctx) } -fn add_vis(ctx: AssistCtx) -> Option { +fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, _ => false, @@ -66,15 +66,10 @@ fn add_vis(ctx: AssistCtx) -> Option { return None; }; - ctx.add_assist( - AssistId("change_visibility"), - "Change visibility to pub(crate)", - target, - |edit| { - edit.insert(offset, "pub(crate) "); - edit.set_cursor(offset); - }, - ) + acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { + edit.insert(offset, "pub(crate) "); + edit.set_cursor(offset); + }) } fn vis_offset(node: &SyntaxNode) -> TextSize { @@ -88,10 +83,10 @@ fn vis_offset(node: &SyntaxNode) -> TextSize { .unwrap_or_else(|| node.text_range().start()) } -fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option { +fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { if vis.syntax().text() == "pub" { let target = vis.syntax().text_range(); - return ctx.add_assist( + return acc.add( AssistId("change_visibility"), "Change Visibility to pub(crate)", target, @@ -103,7 +98,7 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option { } if vis.syntax().text() == "pub(crate)" { let target = vis.syntax().text_range(); - return ctx.add_assist( + return acc.add( AssistId("change_visibility"), "Change visibility to pub", target, diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4bd6040b2..ccf91797c 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -9,7 +9,7 @@ use ra_syntax::{ }; use crate::{ - assist_ctx::{Assist, AssistCtx}, + assist_context::{AssistContext, Assists}, utils::invert_boolean_expression, AssistId, }; @@ -36,7 +36,7 @@ use crate::{ // bar(); // } // ``` -pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option { +pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; if if_expr.else_branch().is_some() { return None; @@ -96,93 +96,88 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option { let cursor_position = ctx.frange.range.start(); let target = if_expr.syntax().text_range(); - ctx.add_assist( - AssistId("convert_to_guarded_return"), - "Convert to guarded return", - target, - |edit| { - let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); - let new_block = match if_let_pat { - None => { - // If. - let new_expr = { - let then_branch = - make::block_expr(once(make::expr_stmt(early_expression).into()), None); - let cond = invert_boolean_expression(cond_expr); - let e = make::expr_if(make::condition(cond, None), then_branch); - if_indent_level.increase_indent(e) - }; - replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) - } - Some((path, bound_ident)) => { - // If-let. - let match_expr = { - let happy_arm = { - let pat = make::tuple_struct_pat( - path, - once(make::bind_pat(make::name("it")).into()), - ); - let expr = { - let name_ref = make::name_ref("it"); - let segment = make::path_segment(name_ref); - let path = make::path_unqualified(segment); - make::expr_path(path) - }; - make::match_arm(once(pat.into()), expr) - }; - - let sad_arm = make::match_arm( - // FIXME: would be cool to use `None` or `Err(_)` if appropriate - once(make::placeholder_pat().into()), - early_expression, + acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { + let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); + let new_block = match if_let_pat { + None => { + // If. + let new_expr = { + let then_branch = + make::block_expr(once(make::expr_stmt(early_expression).into()), None); + let cond = invert_boolean_expression(cond_expr); + let e = make::expr_if(make::condition(cond, None), then_branch); + if_indent_level.increase_indent(e) + }; + replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) + } + Some((path, bound_ident)) => { + // If-let. + let match_expr = { + let happy_arm = { + let pat = make::tuple_struct_pat( + path, + once(make::bind_pat(make::name("it")).into()), ); - - make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) + let expr = { + let name_ref = make::name_ref("it"); + let segment = make::path_segment(name_ref); + let path = make::path_unqualified(segment); + make::expr_path(path) + }; + make::match_arm(once(pat.into()), expr) }; - let let_stmt = make::let_stmt( - make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), - Some(match_expr), + let sad_arm = make::match_arm( + // FIXME: would be cool to use `None` or `Err(_)` if appropriate + once(make::placeholder_pat().into()), + early_expression, ); - let let_stmt = if_indent_level.increase_indent(let_stmt); - replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) - } - }; - edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); - edit.set_cursor(cursor_position); - - fn replace( - new_expr: &SyntaxNode, - then_block: &ast::BlockExpr, - parent_block: &ast::BlockExpr, - if_expr: &ast::IfExpr, - ) -> SyntaxNode { - let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); - let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); - let end_of_then = - if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { - end_of_then.prev_sibling_or_token().unwrap() - } else { - end_of_then - }; - let mut then_statements = new_expr.children_with_tokens().chain( - then_block_items - .syntax() - .children_with_tokens() - .skip(1) - .take_while(|i| *i != end_of_then), + + make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) + }; + + let let_stmt = make::let_stmt( + make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), + Some(match_expr), ); - replace_children( - &parent_block.syntax(), - RangeInclusive::new( - if_expr.clone().syntax().clone().into(), - if_expr.syntax().clone().into(), - ), - &mut then_statements, - ) + let let_stmt = if_indent_level.increase_indent(let_stmt); + replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) } - }, - ) + }; + edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); + edit.set_cursor(cursor_position); + + fn replace( + new_expr: &SyntaxNode, + then_block: &ast::BlockExpr, + parent_block: &ast::BlockExpr, + if_expr: &ast::IfExpr, + ) -> SyntaxNode { + let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); + let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); + let end_of_then = + if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { + end_of_then.prev_sibling_or_token().unwrap() + } else { + end_of_then + }; + let mut then_statements = new_expr.children_with_tokens().chain( + then_block_items + .syntax() + .children_with_tokens() + .skip(1) + .take_while(|i| *i != end_of_then), + ); + replace_children( + &parent_block.syntax(), + RangeInclusive::new( + if_expr.clone().syntax().clone().into(), + if_expr.syntax().clone().into(), + ), + &mut then_statements, + ) + } + }) } #[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 7c8f8bdf2..13c1e7e80 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: fill_match_arms // @@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option { +pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_expr = ctx.find_node_at_offset::()?; let match_arm_list = match_expr.match_arm_list()?; @@ -93,7 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option { } let target = match_expr.syntax().text_range(); - ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { + acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); edit.set_cursor(expr.syntax().text_range().start()); edit.replace_ast(match_arm_list, new_arm_list); diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index cb7264d7b..692ba4895 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs @@ -1,6 +1,6 @@ use ra_syntax::ast::{AstNode, BinExpr, BinOp}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_binexpr // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // let _ = 2 + 90; // } // ``` -pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option { +pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let expr = ctx.find_node_at_offset::()?; let lhs = expr.lhs()?.syntax().clone(); let rhs = expr.rhs()?.syntax().clone(); @@ -33,7 +33,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { + acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { if let FlipAction::FlipAndReplaceOp(new_op) = action { edit.replace(op_range, new_op); } diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index 24982ae22..dfe2a7fed 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs @@ -1,6 +1,6 @@ use ra_syntax::{algo::non_trivia_sibling, Direction, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_comma // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ((3, 4), (1, 2)); // } // ``` -pub(crate) fn flip_comma(ctx: AssistCtx) -> Option { +pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let comma = ctx.find_token_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; @@ -28,7 +28,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { + acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { edit.replace(prev.text_range(), next.to_string()); edit.replace(next.text_range(), prev.to_string()); }) diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 6a3b2df67..8a08702ab 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::{ Direction, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_trait_bound // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // fn foo() { } // ``` -pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option { +pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { // We want to replicate the behavior of `flip_binexpr` by only suggesting // the assist when the cursor is on a `+` let plus = ctx.find_token_at_offset(T![+])?; @@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option { ); let target = plus.text_range(); - ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { + acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { edit.replace(before.text_range(), after.to_string()); edit.replace(after.text_range(), before.to_string()); }) diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index e5765c845..5b26814d3 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs @@ -5,7 +5,10 @@ use ra_syntax::{ }; use test_utils::tested_by; -use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: inline_local_variable // @@ -23,7 +26,7 @@ use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; // (1 + 2) * 4; // } // ``` -pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { +pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let let_stmt = ctx.find_node_at_offset::()?; let bind_pat = match let_stmt.pat()? { ast::Pat::BindPat(pat) => pat, @@ -33,7 +36,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { tested_by!(test_not_inline_mut_variable); return None; } - if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) { + if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { tested_by!(not_applicable_outside_of_bind_pat); return None; } @@ -107,20 +110,14 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { let init_in_paren = format!("({})", &init_str); let target = bind_pat.syntax().text_range(); - ctx.add_assist( - AssistId("inline_local_variable"), - "Inline variable", - target, - move |edit: &mut ActionBuilder| { - edit.delete(delete_range); - for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { - let replacement = - if should_wrap { init_in_paren.clone() } else { init_str.clone() }; - edit.replace(desc.file_range.range, replacement) - } - edit.set_cursor(delete_range.start()) - }, - ) + acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| { + builder.delete(delete_range); + for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { + let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; + builder.replace(desc.file_range.range, replacement) + } + builder.set_cursor(delete_range.start()) + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 3c340ff3b..fdf3ada0d 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs @@ -9,7 +9,7 @@ use ra_syntax::{ use stdx::format_to; use test_utils::tested_by; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: introduce_variable // @@ -27,7 +27,7 @@ use crate::{Assist, AssistCtx, AssistId}; // var_name * 4; // } // ``` -pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option { +pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { if ctx.frange.range.is_empty() { return None; } @@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option { return None; } let target = expr.syntax().text_range(); - ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { + acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { let mut buf = String::new(); let cursor_offset = if wrap_in_block { diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs index b16271443..527c7caef 100644 --- a/crates/ra_assists/src/handlers/invert_if.rs +++ b/crates/ra_assists/src/handlers/invert_if.rs @@ -3,7 +3,11 @@ use ra_syntax::{ T, }; -use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::invert_boolean_expression, + AssistId, +}; // Assist: invert_if // @@ -24,7 +28,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; // } // ``` -pub(crate) fn invert_if(ctx: AssistCtx) -> Option { +pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_keyword = ctx.find_token_at_offset(T![if])?; let expr = ast::IfExpr::cast(if_keyword.parent())?; let if_range = if_keyword.text_range(); @@ -40,21 +44,21 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option { let cond = expr.condition()?.expr()?; let then_node = expr.then_branch()?.syntax().clone(); + let else_block = match expr.else_branch()? { + ast::ElseBranch::Block(it) => it, + ast::ElseBranch::IfExpr(_) => return None, + }; - if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { - let cond_range = cond.syntax().text_range(); - let flip_cond = invert_boolean_expression(cond); - let else_node = else_block.syntax(); - let else_range = else_node.text_range(); - let then_range = then_node.text_range(); - return ctx.add_assist(AssistId("invert_if"), "Invert if", if_range, |edit| { - edit.replace(cond_range, flip_cond.syntax().text()); - edit.replace(else_range, then_node.text()); - edit.replace(then_range, else_node.text()); - }); - } - - None + let cond_range = cond.syntax().text_range(); + let flip_cond = invert_boolean_expression(cond); + let else_node = else_block.syntax(); + let else_range = else_node.text_range(); + let then_range = then_node.text_range(); + acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| { + edit.replace(cond_range, flip_cond.syntax().text()); + edit.replace(else_range, then_node.text()); + edit.replace(then_range, else_node.text()); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index de74d83d8..8e1d93312 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs @@ -6,7 +6,10 @@ use ra_syntax::{ AstNode, Direction, InsertPosition, SyntaxElement, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: merge_imports // @@ -20,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn merge_imports(ctx: AssistCtx) -> Option { +pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let tree: ast::UseTree = ctx.find_node_at_offset()?; let mut rewriter = SyntaxRewriter::default(); let mut offset = ctx.frange.range.start(); @@ -53,10 +56,10 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option { }; let target = tree.syntax().text_range(); - ctx.add_assist(AssistId("merge_imports"), "Merge imports", target, |edit| { - edit.rewrite(rewriter); + acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { + builder.rewrite(rewriter); // FIXME: we only need because our diff is imprecise - edit.set_cursor(offset); + builder.set_cursor(offset); }) } diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index 7c4d9d55d..cfe4df47b 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs @@ -6,7 +6,7 @@ use ra_syntax::{ Direction, TextSize, }; -use crate::{Assist, AssistCtx, AssistId, TextRange}; +use crate::{AssistContext, AssistId, Assists, TextRange}; // Assist: merge_match_arms // @@ -32,7 +32,7 @@ use crate::{Assist, AssistCtx, AssistId, TextRange}; // } // } // ``` -pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option { +pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let current_arm = ctx.find_node_at_offset::()?; // Don't try to handle arms with guards for now - can add support for this later if current_arm.guard().is_some() { @@ -70,7 +70,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { + acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { let pats = if arms_to_merge.iter().any(contains_placeholder) { "_".into() } else { diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs index 44e50cb6e..a41aacfc3 100644 --- a/crates/ra_assists/src/handlers/move_bounds.rs +++ b/crates/ra_assists/src/handlers/move_bounds.rs @@ -5,7 +5,7 @@ use ra_syntax::{ T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: move_bounds_to_where_clause // @@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId}; // f(x) // } // ``` -pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option { +pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let type_param_list = ctx.find_node_at_offset::()?; let mut type_params = type_param_list.type_params(); @@ -50,36 +50,29 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option { }; let target = type_param_list.syntax().text_range(); - ctx.add_assist( - AssistId("move_bounds_to_where_clause"), - "Move to where clause", - target, - |edit| { - let new_params = type_param_list - .type_params() - .filter(|it| it.type_bound_list().is_some()) - .map(|type_param| { - let without_bounds = type_param.remove_bounds(); - (type_param, without_bounds) - }); - - let new_type_param_list = type_param_list.replace_descendants(new_params); - edit.replace_ast(type_param_list.clone(), new_type_param_list); - - let where_clause = { - let predicates = type_param_list.type_params().filter_map(build_predicate); - make::where_clause(predicates) - }; - - let to_insert = match anchor.prev_sibling_or_token() { - Some(ref elem) if elem.kind() == WHITESPACE => { - format!("{} ", where_clause.syntax()) - } - _ => format!(" {}", where_clause.syntax()), - }; - edit.insert(anchor.text_range().start(), to_insert); - }, - ) + acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| { + let new_params = type_param_list + .type_params() + .filter(|it| it.type_bound_list().is_some()) + .map(|type_param| { + let without_bounds = type_param.remove_bounds(); + (type_param, without_bounds) + }); + + let new_type_param_list = type_param_list.replace_descendants(new_params); + edit.replace_ast(type_param_list.clone(), new_type_param_list); + + let where_clause = { + let predicates = type_param_list.type_params().filter_map(build_predicate); + make::where_clause(predicates) + }; + + let to_insert = match anchor.prev_sibling_or_token() { + Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), + _ => format!(" {}", where_clause.syntax()), + }; + edit.insert(anchor.text_range().start(), to_insert); + }) } fn build_predicate(param: ast::TypeParam) -> Option { diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index 29bc9a9ff..fc0335b57 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: move_guard_to_arm_body // @@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { +pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_arm = ctx.find_node_at_offset::()?; let guard = match_arm.guard()?; let space_before_guard = guard.syntax().prev_sibling_or_token(); @@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); let target = guard.syntax().text_range(); - ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { + acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { Some(tok) => { if ast::Whitespace::cast(tok.clone()).is_some() { @@ -88,7 +88,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { // } // } // ``` -pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option { +pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; @@ -109,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option { let buf = format!(" if {}", cond.syntax().text()); let target = if_expr.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("move_arm_cond_to_match_guard"), "Move condition to match guard", target, diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index 155c679b4..c20ffe0b3 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs @@ -5,7 +5,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: make_raw_string // @@ -22,11 +22,11 @@ use crate::{Assist, AssistCtx, AssistId}; // r#"Hello, World!"#; // } // ``` -pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option { +pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; let value = token.value()?; let target = token.syntax().text_range(); - ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { + acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { let max_hash_streak = count_hashes(&value); let mut hashes = String::with_capacity(max_hash_streak + 1); for _ in 0..hashes.capacity() { @@ -51,11 +51,11 @@ pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option { // "Hello, \"World!\""; // } // ``` -pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option { +pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; let value = token.value()?; let target = token.syntax().text_range(); - ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { + acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { // parse inside string to escape `"` let escaped = value.escape_default().to_string(); edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); @@ -77,10 +77,10 @@ pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option { // r##"Hello, World!"##; // } // ``` -pub(crate) fn add_hash(ctx: AssistCtx) -> Option { +pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING)?; let target = token.text_range(); - ctx.add_assist(AssistId("add_hash"), "Add # to raw string", target, |edit| { + acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| { edit.insert(token.text_range().start() + TextSize::of('r'), "#"); edit.insert(token.text_range().end(), "#"); }) @@ -101,7 +101,7 @@ pub(crate) fn add_hash(ctx: AssistCtx) -> Option { // r"Hello, World!"; // } // ``` -pub(crate) fn remove_hash(ctx: AssistCtx) -> Option { +pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING)?; let text = token.text().as_str(); if text.starts_with("r\"") { @@ -109,7 +109,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx) -> Option { return None; } let target = token.text_range(); - ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { + acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { let result = &text[2..text.len() - 1]; let result = if result.starts_with('\"') { // FIXME: this logic is wrong, not only the last has has to handled specially diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs index e6e02f2ae..8eef578cf 100644 --- a/crates/ra_assists/src/handlers/remove_dbg.rs +++ b/crates/ra_assists/src/handlers/remove_dbg.rs @@ -3,7 +3,7 @@ use ra_syntax::{ TextSize, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: remove_dbg // @@ -20,7 +20,7 @@ use crate::{Assist, AssistCtx, AssistId}; // 92; // } // ``` -pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option { +pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let macro_call = ctx.find_node_at_offset::()?; if !is_valid_macrocall(¯o_call, "dbg")? { @@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option { }; let target = macro_call.syntax().text_range(); - ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { + acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { edit.replace(macro_range, macro_content); edit.set_cursor(cursor_pos); }) diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs index 9f72f879d..dce546db7 100644 --- a/crates/ra_assists/src/handlers/remove_mut.rs +++ b/crates/ra_assists/src/handlers/remove_mut.rs @@ -1,6 +1,6 @@ use ra_syntax::{SyntaxKind, TextRange, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: remove_mut // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // fn feed(&self, amount: u32) {} // } // ``` -pub(crate) fn remove_mut(ctx: AssistCtx) -> Option { +pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let mut_token = ctx.find_token_at_offset(T![mut])?; let delete_from = mut_token.text_range().start(); let delete_to = match mut_token.next_token() { @@ -26,7 +26,7 @@ pub(crate) fn remove_mut(ctx: AssistCtx) -> Option { }; let target = mut_token.text_range(); - ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { + acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { edit.set_cursor(delete_from); edit.delete(TextRange::new(delete_from, delete_to)); }) diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 0b930dea2..757f6406e 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs @@ -3,18 +3,9 @@ use std::collections::HashMap; use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; use itertools::Itertools; use ra_ide_db::RootDatabase; -use ra_syntax::{ - algo, - ast::{self, Path, RecordLit, RecordPat}, - match_ast, AstNode, SyntaxKind, - SyntaxKind::*, - SyntaxNode, -}; - -use crate::{ - assist_ctx::{Assist, AssistCtx}, - AssistId, -}; +use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; + +use crate::{AssistContext, AssistId, Assists}; // Assist: reorder_fields // @@ -31,13 +22,13 @@ use crate::{ // const test: Foo = Foo {foo: 1, bar: 0} // ``` // -pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option { - reorder::(ctx.clone()).or_else(|| reorder::(ctx)) +pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + reorder::(acc, ctx.clone()).or_else(|| reorder::(acc, ctx)) } -fn reorder(ctx: AssistCtx) -> Option { +fn reorder(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let record = ctx.find_node_at_offset::()?; - let path = record.syntax().children().find_map(Path::cast)?; + let path = record.syntax().children().find_map(ast::Path::cast)?; let ranks = compute_fields_ranks(&path, &ctx)?; @@ -51,7 +42,7 @@ fn reorder(ctx: AssistCtx) -> Option { } let target = record.syntax().text_range(); - ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { + acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { for (old, new) in fields.iter().zip(&sorted_fields) { algo::diff(old, new).into_text_edit(edit.text_edit_builder()); } @@ -96,9 +87,9 @@ fn struct_definition(path: &ast::Path, sema: &Semantics) -> Option } } -fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option> { +fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option> { Some( - struct_definition(path, ctx.sema)? + struct_definition(path, &ctx.sema)? .fields(ctx.db) .iter() .enumerate() diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 2eb8348f8..a59a06efa 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -4,7 +4,7 @@ use ra_syntax::{ AstNode, }; -use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_if_let_with_match // @@ -32,7 +32,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option { +pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; let cond = if_expr.condition()?; let pat = cond.pat()?; @@ -43,36 +43,31 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option { ast::ElseBranch::IfExpr(_) => return None, }; - let sema = ctx.sema; let target = if_expr.syntax().text_range(); - ctx.add_assist( - AssistId("replace_if_let_with_match"), - "Replace with match", - target, - move |edit| { - let match_expr = { - let then_arm = { - let then_expr = unwrap_trivial_block(then_block); - make::match_arm(vec![pat.clone()], then_expr) - }; - let else_arm = { - let pattern = sema - .type_of_pat(&pat) - .and_then(|ty| TryEnum::from_ty(sema, &ty)) - .map(|it| it.sad_pattern()) - .unwrap_or_else(|| make::placeholder_pat().into()); - let else_expr = unwrap_trivial_block(else_block); - make::match_arm(vec![pattern], else_expr) - }; - make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) + acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { + let match_expr = { + let then_arm = { + let then_expr = unwrap_trivial_block(then_block); + make::match_arm(vec![pat.clone()], then_expr) }; + let else_arm = { + let pattern = ctx + .sema + .type_of_pat(&pat) + .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) + .map(|it| it.sad_pattern()) + .unwrap_or_else(|| make::placeholder_pat().into()); + let else_expr = unwrap_trivial_block(else_block); + make::match_arm(vec![pattern], else_expr) + }; + make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) + }; - let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); + let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); - edit.set_cursor(if_expr.syntax().text_range().start()); - edit.replace_ast::(if_expr.into(), match_expr); - }, - ) + edit.set_cursor(if_expr.syntax().text_range().start()); + edit.replace_ast::(if_expr.into(), match_expr); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index a5509a567..d3f214591 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -9,11 +9,7 @@ use ra_syntax::{ AstNode, T, }; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::TryEnum, - AssistId, -}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_let_with_if_let // @@ -39,16 +35,16 @@ use crate::{ // // fn compute() -> Option { None } // ``` -pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option { +pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let let_kw = ctx.find_token_at_offset(T![let])?; let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; let init = let_stmt.initializer()?; let original_pat = let_stmt.pat()?; let ty = ctx.sema.type_of_expr(&init)?; - let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case()); + let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case()); let target = let_kw.text_range(); - ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { + acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { let with_placeholder: ast::Pat = match happy_variant { None => make::placeholder_pat().into(), Some(var_name) => make::tuple_struct_pat( diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index fd41da64b..1a81d8a0e 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,11 +1,7 @@ use hir; use ra_syntax::{ast, AstNode, SmolStr, TextRange}; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::insert_use_statement, - AssistId, -}; +use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; // Assist: replace_qualified_name_with_use // @@ -20,7 +16,10 @@ use crate::{ // // fn process(map: HashMap) {} // ``` -pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option { +pub(crate) fn replace_qualified_name_with_use( + acc: &mut Assists, + ctx: &AssistContext, +) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { @@ -34,18 +33,18 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option } let target = path.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("replace_qualified_name_with_use"), "Replace qualified path with use", target, - |edit| { + |builder| { let path_to_import = hir_path.mod_path().clone(); - insert_use_statement(path.syntax(), &path_to_import, edit); + insert_use_statement(path.syntax(), &path_to_import, ctx, builder); if let Some(last) = path.segment() { // Here we are assuming the assist will provide a correct use statement // so we can delete the path qualifier - edit.delete(TextRange::new( + builder.delete(TextRange::new( path.syntax().text_range().start(), last.syntax().text_range().start(), )); diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index c6b73da67..a46998b8e 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -5,7 +5,7 @@ use ra_syntax::{ AstNode, }; -use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_unwrap_with_match // @@ -29,7 +29,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; // }; // } // ``` -pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option { +pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let name = method_call.name_ref()?; if name.text() != "unwrap" { @@ -37,33 +37,26 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option { } let caller = method_call.expr()?; let ty = ctx.sema.type_of_expr(&caller)?; - let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case(); + let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); let target = method_call.syntax().text_range(); - ctx.add_assist( - AssistId("replace_unwrap_with_match"), - "Replace unwrap with match", - target, - |edit| { - let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); - let it = make::bind_pat(make::name("a")).into(); - let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); + acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { + let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); + let it = make::bind_pat(make::name("a")).into(); + let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); - let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); - let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); + let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); + let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); - let unreachable_call = make::unreachable_macro_call().into(); - let err_arm = - make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); + let unreachable_call = make::unreachable_macro_call().into(); + let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); - let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); - let match_expr = make::expr_match(caller.clone(), match_arm_list); - let match_expr = - IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); + let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); + let match_expr = make::expr_match(caller.clone(), match_arm_list); + let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); - edit.set_cursor(caller.syntax().text_range().start()); - edit.replace_ast::(method_call.into(), match_expr); - }, - ) + edit.set_cursor(caller.syntax().text_range().start()); + edit.replace_ast::(method_call.into(), match_expr); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index d49563974..159033731 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs @@ -2,7 +2,7 @@ use std::iter::successors; use ra_syntax::{ast, AstNode, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: split_import // @@ -15,7 +15,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // use std::{collections::HashMap}; // ``` -pub(crate) fn split_import(ctx: AssistCtx) -> Option { +pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let colon_colon = ctx.find_token_at_offset(T![::])?; let path = ast::Path::cast(colon_colon.parent())?.qualifier()?; let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; @@ -29,7 +29,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option { let cursor = ctx.frange.range.start(); let target = colon_colon.text_range(); - ctx.add_assist(AssistId("split_import"), "Split import", target, |edit| { + acc.add(AssistId("split_import"), "Split import", target, |edit| { edit.replace_ast(use_tree, new_tree); edit.set_cursor(cursor); }) diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 6df927abb..eba0631a4 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,4 +1,4 @@ -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; use ast::LoopBodyOwner; use ra_fmt::unwrap_trivial_block; @@ -21,7 +21,7 @@ use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let l_curly_token = ctx.find_token_at_offset(T!['{'])?; let block = ast::BlockExpr::cast(l_curly_token.parent())?; let parent = block.syntax().parent()?; @@ -58,7 +58,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }; let target = expr_to_unwrap.syntax().text_range(); - ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| { + acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { edit.set_cursor(expr.syntax().text_range().start()); let pat_start: &[_] = &[' ', '{', '\n']; diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 0473fd8c2..011613762 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -10,7 +10,7 @@ macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; } -mod assist_ctx; +mod assist_context; mod marks; #[cfg(test)] mod tests; @@ -22,7 +22,7 @@ use ra_db::FileRange; use ra_ide_db::{source_change::SourceChange, RootDatabase}; use ra_syntax::TextRange; -pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; +pub(crate) use crate::assist_context::{AssistContext, Assists}; /// Unique identifier of the assist, should not be shown to the user /// directly. @@ -68,13 +68,12 @@ pub struct ResolvedAssist { /// returned, without actual edits. pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec { let sema = Semantics::new(db); - let ctx = AssistCtx::new(&sema, range, false); - handlers::all() - .iter() - .filter_map(|f| f(ctx.clone())) - .flat_map(|it| it.0) - .map(|a| a.label) - .collect() + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_unresolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_unresolved() } /// Return all the assists applicable at the given position. @@ -83,31 +82,30 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec Vec { let sema = Semantics::new(db); - let ctx = AssistCtx::new(&sema, range, true); - let mut a = handlers::all() - .iter() - .filter_map(|f| f(ctx.clone())) - .flat_map(|it| it.0) - .map(|it| it.into_resolved().unwrap()) - .collect::>(); - a.sort_by_key(|it| it.label.target.len()); - a + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_resolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_resolved() } mod handlers { - use crate::{Assist, AssistCtx}; + use crate::{AssistContext, Assists}; - pub(crate) type Handler = fn(AssistCtx) -> Option; + pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; mod add_custom_impl; mod add_derive; mod add_explicit_type; + mod add_from_impl_for_enum; mod add_function; mod add_impl; mod add_missing_impl_members; mod add_new; mod apply_demorgan; mod auto_import; + mod change_return_type_to_result; mod change_visibility; mod early_return; mod fill_match_arms; @@ -124,14 +122,12 @@ mod handlers { mod raw_string; mod remove_dbg; mod remove_mut; + mod reorder_fields; mod replace_if_let_with_match; mod replace_let_with_if_let; mod replace_qualified_name_with_use; mod replace_unwrap_with_match; mod split_import; - mod change_return_type_to_result; - mod add_from_impl_for_enum; - mod reorder_fields; mod unwrap_block; pub(crate) fn all() -> &'static [Handler] { diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 17e3ece9f..45b2d9733 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -11,7 +11,7 @@ use test_utils::{ RangeOrOffset, }; -use crate::{handlers::Handler, resolved_assists, AssistCtx}; +use crate::{handlers::Handler, resolved_assists, AssistContext, Assists}; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { let (mut db, file_id) = RootDatabase::with_single_file(text); @@ -71,7 +71,7 @@ enum ExpectedResult<'a> { Target(&'a str), } -fn check(assist: Handler, before: &str, expected: ExpectedResult) { +fn check(handler: Handler, before: &str, expected: ExpectedResult) { let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { let (mut db, position) = RootDatabase::with_position(before); db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)])); @@ -90,17 +90,20 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; let sema = Semantics::new(&db); - let assist_ctx = AssistCtx::new(&sema, frange, true); - - match (assist(assist_ctx), expected) { + let ctx = AssistContext::new(sema, frange); + let mut acc = Assists::new_resolved(&ctx); + handler(&mut acc, &ctx); + let mut res = acc.finish_resolved(); + let assist = res.pop(); + match (assist, expected) { (Some(assist), ExpectedResult::After(after)) => { - let mut action = assist.0[0].source_change.clone().unwrap(); - let change = action.source_file_edits.pop().unwrap(); + let mut source_change = assist.source_change; + let change = source_change.source_file_edits.pop().unwrap(); let mut actual = db.file_text(change.file_id).as_ref().to_owned(); change.edit.apply(&mut actual); - match action.cursor_position { + match source_change.cursor_position { None => { if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { let off = change @@ -116,7 +119,7 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { assert_eq_text!(after, &actual); } (Some(assist), ExpectedResult::Target(target)) => { - let range = assist.0[0].label.target; + let range = assist.label.target; assert_eq_text!(&text_without_caret[range], target); } (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index c1f447efe..1214e3cd4 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -2,7 +2,6 @@ // FIXME: rewrite according to the plan, outlined in // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 -use crate::assist_ctx::ActionBuilder; use hir::{self, ModPath}; use ra_syntax::{ ast::{self, NameOwner}, @@ -12,6 +11,8 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; +use crate::assist_context::{AssistBuilder, AssistContext}; + /// Creates and inserts a use statement for the given path to import. /// The use statement is inserted in the scope most appropriate to the /// the cursor position given, additionally merged with the existing use imports. @@ -19,10 +20,11 @@ pub(crate) fn insert_use_statement( // Ideally the position of the cursor, used to position: &SyntaxNode, path_to_import: &ModPath, - edit: &mut ActionBuilder, + ctx: &AssistContext, + builder: &mut AssistBuilder, ) { let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); - let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| { + let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { if let Some(module) = ast::Module::cast(n.clone()) { return module.item_list().map(|it| it.syntax().clone()); } @@ -31,7 +33,7 @@ pub(crate) fn insert_use_statement( if let Some(container) = container { let action = best_action_for_target(container, position.clone(), &target); - make_assist(&action, &target, edit.text_edit_builder()); + make_assist(&action, &target, builder.text_edit_builder()); } } -- cgit v1.2.3