]> git.lizzy.rs Git - rust.git/commitdiff
Refactor assists API to be more convenient for adding new assists
authorAleksey Kladov <aleksey.kladov@gmail.com>
Wed, 6 May 2020 16:45:35 +0000 (18:45 +0200)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Thu, 7 May 2020 14:27:54 +0000 (16:27 +0200)
It now duplicates completion API in its shape.

39 files changed:
crates/ra_assists/src/assist_context.rs [new file with mode: 0644]
crates/ra_assists/src/assist_ctx.rs [deleted file]
crates/ra_assists/src/handlers/add_custom_impl.rs
crates/ra_assists/src/handlers/add_derive.rs
crates/ra_assists/src/handlers/add_explicit_type.rs
crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
crates/ra_assists/src/handlers/add_function.rs
crates/ra_assists/src/handlers/add_impl.rs
crates/ra_assists/src/handlers/add_missing_impl_members.rs
crates/ra_assists/src/handlers/add_new.rs
crates/ra_assists/src/handlers/apply_demorgan.rs
crates/ra_assists/src/handlers/auto_import.rs
crates/ra_assists/src/handlers/change_return_type_to_result.rs
crates/ra_assists/src/handlers/change_visibility.rs
crates/ra_assists/src/handlers/early_return.rs
crates/ra_assists/src/handlers/fill_match_arms.rs
crates/ra_assists/src/handlers/flip_binexpr.rs
crates/ra_assists/src/handlers/flip_comma.rs
crates/ra_assists/src/handlers/flip_trait_bound.rs
crates/ra_assists/src/handlers/inline_local_variable.rs
crates/ra_assists/src/handlers/introduce_variable.rs
crates/ra_assists/src/handlers/invert_if.rs
crates/ra_assists/src/handlers/merge_imports.rs
crates/ra_assists/src/handlers/merge_match_arms.rs
crates/ra_assists/src/handlers/move_bounds.rs
crates/ra_assists/src/handlers/move_guard.rs
crates/ra_assists/src/handlers/raw_string.rs
crates/ra_assists/src/handlers/remove_dbg.rs
crates/ra_assists/src/handlers/remove_mut.rs
crates/ra_assists/src/handlers/reorder_fields.rs
crates/ra_assists/src/handlers/replace_if_let_with_match.rs
crates/ra_assists/src/handlers/replace_let_with_if_let.rs
crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
crates/ra_assists/src/handlers/split_import.rs
crates/ra_assists/src/handlers/unwrap_block.rs
crates/ra_assists/src/lib.rs
crates/ra_assists/src/tests.rs
crates/ra_assists/src/utils/insert_use.rs

diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
new file mode 100644 (file)
index 0000000..203ad12
--- /dev/null
@@ -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<SyntaxToken> {
+        self.source_file.syntax().token_at_offset(self.offset())
+    }
+    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
+        self.token_at_offset().find(|it| it.kind() == kind)
+    }
+    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
+        find_node_at_offset(self.source_file.syntax(), self.offset())
+    }
+    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
+        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<SourceChange>)>,
+}
+
+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<AssistLabel> {
+        assert!(!self.resolve);
+        self.finish()
+            .into_iter()
+            .map(|(label, edit)| {
+                assert!(edit.is_none());
+                label
+            })
+            .collect()
+    }
+
+    pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
+        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<String>,
+        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<String>,
+        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<SourceChange>)> {
+        self.buf.sort_by_key(|(label, _edit)| label.target.len());
+        self.buf
+    }
+}
+
+pub(crate) struct AssistBuilder {
+    edit: TextEditBuilder,
+    cursor_position: Option<TextSize>,
+    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<String>) {
+        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<String>) {
+        self.edit.replace(range, replace_with.into())
+    }
+    pub(crate) fn replace_ast<N: AstNode>(&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<String>,
+    ) {
+        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 (file)
index 871671d..0000000
+++ /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<AssistInfo>);
-
-#[derive(Clone, Debug)]
-pub(crate) struct AssistInfo {
-    pub(crate) label: AssistLabel,
-    pub(crate) group_label: Option<GroupLabel>,
-    pub(crate) source_change: Option<SourceChange>,
-}
-
-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<ResolvedAssist> {
-        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<String>,
-        target: TextRange,
-        f: impl FnOnce(&mut ActionBuilder),
-    ) -> Option<Assist> {
-        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<String>) -> AssistGroup<'a> {
-        let group = GroupLabel(group_name.into());
-        AssistGroup { ctx: self, group, assists: Vec::new() }
-    }
-
-    pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
-        self.source_file.syntax().token_at_offset(self.frange.range.start())
-    }
-
-    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
-        self.token_at_offset().find(|it| it.kind() == kind)
-    }
-
-    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
-        find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
-    }
-
-    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
-        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<AssistInfo>,
-}
-
-impl<'a> AssistGroup<'a> {
-    pub(crate) fn add_assist(
-        &mut self,
-        id: AssistId,
-        label: impl Into<String>,
-        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<Assist> {
-        if self.assists.is_empty() {
-            None
-        } else {
-            Some(Assist(self.assists))
-        }
-    }
-}
-
-pub(crate) struct ActionBuilder<'a, 'b> {
-    edit: TextEditBuilder,
-    cursor_position: Option<TextSize>,
-    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<String>) {
-        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<String>,
-    ) {
-        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<String>) {
-        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<N: AstNode>(&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)
-    }
-}
index 869d4dc045881e35547f59996541ac442d43be1f..795a225a4d8fc43ab12043c7ad40207a4ce370bf 100644 (file)
@@ -6,7 +6,10 @@
 };
 use stdx::SepBy;
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{
+    assist_context::{AssistContext, Assists},
+    AssistId,
+};
 
 // Assist: add_custom_impl
 //
@@ -25,7 +28,7 @@
 //
 // }
 // ```
-pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
     let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
 
@@ -49,7 +52,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
         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()
index 2a6bb1caedfea40827e3c1b10dc644bae79a044e..fb08c19e936145203eeb5955848434ce96d2a4bb 100644 (file)
@@ -4,7 +4,7 @@
     TextSize,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_derive
 //
 //     y: u32,
 // }
 // ```
-pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
     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<TextSize> {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::tests::{check_assist, check_assist_target};
 
+    use super::*;
+
     #[test]
     fn add_derive_new() {
         check_assist(
index a59ec16b2d9ff4b6a2da199c1e0426eed98db93c..55409e5013c6300c04740449caae2223d0d24c22 100644 (file)
@@ -4,7 +4,7 @@
     TextRange,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_explicit_type
 //
@@ -21,7 +21,7 @@
 //     let x: i32 = 92;
 // }
 // ```
-pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let stmt = ctx.find_node_at_offset::<LetStmt>()?;
     let expr = stmt.initializer()?;
     let pat = stmt.pat()?;
@@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
 
     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,
index 81deb3dfa3b89828e56a22ac914c83cfe957dbe2..275184e2466ca0db33f98bc1a6aa024fad83b1fb 100644 (file)
@@ -4,10 +4,10 @@
     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 @@
 //     }
 // }
 // ```
-pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
     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<Assist> {
         _ => 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,
index 278079665062d287b4a6e79f7ce76cebd4fca060..6b5616aa9c8d7816e6f07d9e095cc961ec744a74 100644 (file)
@@ -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 @@
 // }
 //
 // ```
-pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     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<hir::InFile<hir::ModuleSource>>,
@@ -152,7 +151,7 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> {
 
 /// Computes the type variables and arguments required for the generated function
 fn fn_args(
-    ctx: &AssistCtx,
+    ctx: &AssistContext,
     call: &ast::CallExpr,
 ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
     let mut arg_names = Vec::new();
@@ -219,7 +218,7 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
     }
 }
 
-fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> {
+fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> {
     let ty = ctx.sema.type_of_expr(fn_arg)?;
     if ty.is_unknown() {
         return None;
index 557344ebb808bafe03ef9d2df27711695b11f5a5..df114a0d84dd40cfc9791ca3c5760834e6fc7e81 100644 (file)
@@ -4,7 +4,7 @@
 };
 use stdx::{format_to, SepBy};
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_impl
 //
 //
 // }
 // ```
-pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
     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)]
index 7df786590904a1516c9f52a7d90972890647c942..3482a75bfcfb2299f313bf6eb01e346a98953888 100644 (file)
@@ -9,9 +9,10 @@
 };
 
 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<Assist> {
+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<Assist> {
 //
 // }
 // ```
-pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
 }
 
 fn add_missing_impl_members_inner(
-    ctx: AssistCtx,
+    acc: &mut Assists,
+    ctx: &AssistContext,
     mode: AddMissingImplMembersMode,
     assist_id: &'static str,
     label: &'static str,
-) -> Option<Assist> {
+) -> Option<()> {
     let _p = ra_prof::profile("add_missing_impl_members_inner");
     let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?;
     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)]
index 1c3f8435ab6c0f4fad3864e1c19225aa357d999b..fe7451dcfdf4186835cb677902e5cae522ba340c 100644 (file)
@@ -7,7 +7,7 @@
 };
 use stdx::{format_to, SepBy};
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_new
 //
@@ -29,7 +29,7 @@
 // }
 //
 // ```
-pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
 
     // 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<Assist> {
     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<Option<ast::ImplDef>> {
+fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
     let db = ctx.db;
     let module = strukt.syntax().ancestors().find(|node| {
         ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
index a5b26e5b937655095aa7d323474fd5ee6c89ed0e..0feba5e11f6510a7dd7e78c9530899fb35b17a2c 100644 (file)
@@ -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 @@
 //     if !(x == 4 && y) {}
 // }
 // ```
-pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
     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<Assist> {
     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()));
index 2224b9714bc2eeff4e9dc4af0ebc04820444ebc0..78d23150d385be1ddc4581da32725da056ea3ef6 100644 (file)
@@ -1,5 +1,6 @@
 use std::collections::BTreeSet;
 
+use either::Either;
 use hir::{
     AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
     Type,
 };
 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 @@
 // }
 // # pub mod std { pub mod collections { pub struct HashMap { } } }
 // ```
-pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     }
 
     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<Self> {
+    fn new(ctx: &AssistContext) -> Option<Self> {
         if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
             Self::for_regular_path(path_under_caret, &ctx)
         } else {
@@ -71,7 +73,7 @@ fn new(ctx: &AssistCtx) -> Option<Self> {
         }
     }
 
-    fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> {
+    fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option<Self> {
         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 @@ fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<
         })
     }
 
-    fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> {
+    fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> {
         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 @@ fn get_search_query(&self) -> &str {
         }
     }
 
-    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 @@ fn get_import_group_message(&self) -> String {
             ImportCandidate::TraitMethod(_, trait_method_name) => {
                 format!("Import a trait for method {}", trait_method_name)
             }
-        }
+        };
+        GroupLabel(name)
     }
 
     fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
@@ -383,7 +386,7 @@ pub mod PubMod3 {
             }
             ",
             r"
-            use PubMod1::PubStruct;
+            use PubMod3::PubStruct;
 
             PubSt<|>ruct
 
index 1e8d986cdbc4f73f66c9035da48a61bc5123239a..5c907097e556a8ba8dbcc757db178a6f1c563632 100644 (file)
@@ -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 @@
 // ```
 // fn foo() -> Result<i32, > { Ok(42i32) }
 // ```
-pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
     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<Assist> {
         return None;
     }
 
-    ctx.add_assist(
+    acc.add(
         AssistId("change_return_type_to_result"),
         "Change return type to Result",
         ret_type.syntax().text_range(),
index 489db83e6eff547c90f693ec3f50d64346a09e11..e631766eff14ab6ae6ca2720ac033ef423b3026d 100644 (file)
@@ -7,10 +7,10 @@
     },
     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.
 // ```
 // pub(crate) fn frobnicate() {}
 // ```
-pub(crate) fn change_visibility(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
-        return change_vis(ctx, vis);
+        return change_vis(acc, vis);
     }
-    add_vis(ctx)
+    add_vis(acc, ctx)
 }
 
-fn add_vis(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
         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<Assist> {
+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<Assist> {
     }
     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,
index 4bd6040b2ad8dd99a99f97b49c04b83108d16164..ccf91797c68737e19548b1d827601e2d01aa0925 100644 (file)
@@ -9,7 +9,7 @@
 };
 
 use crate::{
-    assist_ctx::{Assist, AssistCtx},
+    assist_context::{AssistContext, Assists},
     utils::invert_boolean_expression,
     AssistId,
 };
@@ -36,7 +36,7 @@
 //     bar();
 // }
 // ```
-pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     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)]
index 7c8f8bdf24c395dc3c58ecc69c96abf665271ff3..13c1e7e8014b1317aeebc4992a44a4261943b784 100644 (file)
@@ -5,7 +5,7 @@
 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 @@
 //     }
 // }
 // ```
-pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
     let match_arm_list = match_expr.match_arm_list()?;
 
@@ -93,7 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
     }
 
     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);
index cb7264d7bb347e1830d4fbbcc32a80a87a9082b3..692ba4895cb2e917e428fc1e7355dc65da6cab8c 100644 (file)
@@ -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 @@
 //     let _ = 2 + 90;
 // }
 // ```
-pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let expr = ctx.find_node_at_offset::<BinExpr>()?;
     let lhs = expr.lhs()?.syntax().clone();
     let rhs = expr.rhs()?.syntax().clone();
@@ -33,7 +33,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> {
         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);
         }
index 24982ae22533c01e24b8eb0652a91986a8eba580..dfe2a7fedc0576a5a7adfa6176f9154abc68b95b 100644 (file)
@@ -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 @@
 //     ((3, 4), (1, 2));
 // }
 // ```
-pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
         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());
     })
index 6a3b2df679dccec1f771c9737c460d3a3b6d1ea4..8a08702ab29caf6694f46ebabccde4bf81c2189c 100644 (file)
@@ -4,7 +4,7 @@
     Direction, T,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: flip_trait_bound
 //
@@ -17,7 +17,7 @@
 // ```
 // fn foo<T: Copy + Clone>() { }
 // ```
-pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     );
 
     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());
     })
index e5765c845eedfb18828b255237f33e6c8d429d30..5b26814d30ad3d66ef847abe5b79fb0e555b1ac6 100644 (file)
@@ -5,7 +5,10 @@
 };
 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 @@
 //     (1 + 2) * 4;
 // }
 // ```
-pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
     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<Assist> {
         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<Assist> {
     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)]
index 3c340ff3b9786e9c87442b4035c088d1e4378a90..fdf3ada0d794a8638eeff6ed0f4e9ef92b2401cc 100644 (file)
@@ -9,7 +9,7 @@
 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 @@
 //     var_name * 4;
 // }
 // ```
-pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
         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 {
index b16271443e5d650ec36fcd7eb84905970564c59d..527c7caef1a3bf2c068c17588c4148816b51fa5a 100644 (file)
@@ -3,7 +3,11 @@
     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 @@
 // }
 // ```
 
-pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
 
     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)]
index de74d83d8503aee5eae5469145085e4bed34a194..8e1d93312388bd712298e30c6a63d9dfaae2fad6 100644 (file)
@@ -6,7 +6,10 @@
     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 std::{fmt::Formatter, io};
 // ```
-pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     };
 
     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);
     })
 }
 
index 7c4d9d55d78c27f9b7d7afa4f0e77d358675da2b..cfe4df47bce64d46080969876bde87a80c737734 100644 (file)
@@ -6,7 +6,7 @@
     Direction, TextSize,
 };
 
-use crate::{Assist, AssistCtx, AssistId, TextRange};
+use crate::{AssistContext, AssistId, Assists, TextRange};
 
 // Assist: merge_match_arms
 //
@@ -32,7 +32,7 @@
 //     }
 // }
 // ```
-pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
     // 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 @@ enum CursorPos {
         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 {
index 44e50cb6e6642004edb628bf844ccb7fa3fd1a61..a41aacfc3dc39605282f5dd392692481d14e9ad5 100644 (file)
@@ -5,7 +5,7 @@
     T,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: move_bounds_to_where_clause
 //
@@ -22,7 +22,7 @@
 //     f(x)
 // }
 // ```
-pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
 
     let mut type_params = type_param_list.type_params();
@@ -50,36 +50,29 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
     };
 
     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<ast::WherePred> {
index 29bc9a9ffb41ec90438851555a873e8c45767072..fc0335b5785d909179f9a6fdb2b4876c76521bee 100644 (file)
@@ -4,7 +4,7 @@
     TextSize,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: move_guard_to_arm_body
 //
@@ -31,7 +31,7 @@
 //     }
 // }
 // ```
-pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
     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<Assist> {
     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<Assist> {
 //     }
 // }
 // ```
-pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
     let match_pat = match_arm.pat()?;
 
@@ -109,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
     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,
index 155c679b4b336ad6f96c276f987ef3449f1b7894..c20ffe0b30aba765e97876eb08c18710f0a4fa49 100644 (file)
@@ -5,7 +5,7 @@
     TextSize,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: make_raw_string
 //
 //     r#"Hello, World!"#;
 // }
 // ```
-pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
 //     "Hello, \"World!\"";
 // }
 // ```
-pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
 //     r##"Hello, World!"##;
 // }
 // ```
-pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
 //     r"Hello, World!";
 // }
 // ```
-pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
         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
index e6e02f2aec3dc6217057a02ef5baeb50f0ff66f8..8eef578cf40128ae2fafc658c451ac30150bbe44 100644 (file)
@@ -3,7 +3,7 @@
     TextSize, T,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: remove_dbg
 //
@@ -20,7 +20,7 @@
 //     92;
 // }
 // ```
-pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
 
     if !is_valid_macrocall(&macro_call, "dbg")? {
@@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> {
     };
 
     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);
     })
index 9f72f879d5e0a7cdea620adb30e57b6e6c791e32..dce546db79d5607eb7cc1c9d3edf8ef15c98b6f9 100644 (file)
@@ -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 @@
 //     fn feed(&self, amount: u32) {}
 // }
 // ```
-pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     };
 
     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));
     })
index 0b930dea2153f055bbadced811919cf68e6a5723..757f6406e9185e7f7c05d4963ae536076fd4b293 100644 (file)
@@ -3,18 +3,9 @@
 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
 //
 // const test: Foo = Foo {foo: 1, bar: 0}
 // ```
 //
-pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
-    reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
+pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx))
 }
 
-fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
+fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let record = ctx.find_node_at_offset::<R>()?;
-    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<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
     }
 
     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<RootDatabase>) -> Option
     }
 }
 
-fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> {
+fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<HashMap<String, usize>> {
     Some(
-        struct_definition(path, ctx.sema)?
+        struct_definition(path, &ctx.sema)?
             .fields(ctx.db)
             .iter()
             .enumerate()
index 2eb8348f826d4f8f9b5c949955fe501684660b0f..a59a06efa545abdbbf7c2f4493c0dc2c1c9985ad 100644 (file)
@@ -4,7 +4,7 @@
     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 @@
 //     }
 // }
 // ```
-pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
         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::<ast::Expr>(if_expr.into(), match_expr);
-        },
-    )
+        edit.set_cursor(if_expr.syntax().text_range().start());
+        edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
+    })
 }
 
 #[cfg(test)]
index a5509a56735caff31ae09cec3e6da4638e9d0f92..d3f214591af058404a1ec60d482a8276da060cef 100644 (file)
@@ -9,11 +9,7 @@
     AstNode, T,
 };
 
-use crate::{
-    assist_ctx::{Assist, AssistCtx},
-    utils::TryEnum,
-    AssistId,
-};
+use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
 
 // Assist: replace_let_with_if_let
 //
 //
 // fn compute() -> Option<i32> { None }
 // ```
-pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
+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(
index fd41da64b8d9b6624ae9e871bb7dfec9b714c0c9..1a81d8a0e0215ce3881642e44c21b5709e707dd8 100644 (file)
@@ -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
 //
 //
 // fn process(map: HashMap<String, String>) {}
 // ```
-pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> {
+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<Assist>
     }
 
     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(),
                 ));
index c6b73da67b086ffd26a9b815e38d2546a8b2cf69..a46998b8eb09f11e154b3e53c4639c10052b0f2e 100644 (file)
@@ -5,7 +5,7 @@
     AstNode,
 };
 
-use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
+use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
 
 // Assist: replace_unwrap_with_match
 //
@@ -29,7 +29,7 @@
 //     };
 // }
 // ```
-pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     }
     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::<ast::Expr>(method_call.into(), match_expr);
-        },
-    )
+        edit.set_cursor(caller.syntax().text_range().start());
+        edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
+    })
 }
 
 #[cfg(test)]
index d49563974604286da9ee7173a429716654893927..159033731f0d1ae0b3614a7b17f72176db46c2c8 100644 (file)
@@ -2,7 +2,7 @@
 
 use ra_syntax::{ast, AstNode, T};
 
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{AssistContext, AssistId, Assists};
 
 // Assist: split_import
 //
@@ -15,7 +15,7 @@
 // ```
 // use std::{collections::HashMap};
 // ```
-pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     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);
     })
index 6df927abbdba879ef83334d22aa7560cbce6f0cc..eba0631a4c242c9a0d9544162a79774f60e49ef6 100644 (file)
@@ -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 @@
 //     println!("foo");
 // }
 // ```
-pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
+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<Assist> {
     };
 
     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'];
index 0473fd8c200a2b4f3b212bac69d79f30302ec543..01161376276f7ceadf8ff7fd29a3d09909fa7d41 100644 (file)
@@ -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 @@ macro_rules! eprintln {
 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<AssistLabel> {
     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<AssistLabe
 /// computed.
 pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
     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::<Vec<_>>();
-    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<Assist>;
+    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] {
index 17e3ece9f60868c41f9908c06c589412d5eccc02..45b2d9733e0bb915ad7f925f36912266b2977a0b 100644 (file)
@@ -11,7 +11,7 @@
     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!"),
index c1f447efe7ab3e96e7f914981ca8ab6c65d7a197..1214e3cd47a66492234e0ad9fc1572612f807b37 100644 (file)
@@ -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_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::<Vec<_>>();
-    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());
     }
 }