]> git.lizzy.rs Git - rust.git/blobdiff - crates/ra_assists/src/assist_ctx.rs
ra_assists: assist "providers" can produce multiple assists
[rust.git] / crates / ra_assists / src / assist_ctx.rs
index 6d09bde52af5d58426975b13cc5d9a86316e7392..e9c4f0a23889495ae82c13abc46ec6ba67c3c7ea 100644 (file)
@@ -5,18 +5,19 @@
     SourceFile, TextRange, AstNode, TextUnit, SyntaxNode,
     algo::{find_leaf_at_offset, find_node_at_offset, find_covering_node, LeafAtOffset},
 };
-use ra_ide_api_light::formatting::{leading_indent, reindent};
+use ra_fmt::{leading_indent, reindent};
 
 use crate::{AssistLabel, AssistAction};
 
+#[derive(Clone, Debug)]
 pub(crate) enum Assist {
-    Unresolved(AssistLabel),
-    Resolved(AssistLabel, AssistAction),
+    Unresolved(Vec<AssistLabel>),
+    Resolved(Vec<(AssistLabel, AssistAction)>),
 }
 
 /// `AssistCtx` allows to apply an assist or check if it could be applied.
 ///
-/// Assists use a somewhat overengineered approach, given the current needs. The
+/// 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.
@@ -50,6 +51,7 @@ pub(crate) struct AssistCtx<'a, DB> {
     pub(crate) frange: FileRange,
     source_file: &'a SourceFile,
     should_compute_edit: bool,
+    assist: Assist,
 }
 
 impl<'a, DB> Clone for AssistCtx<'a, DB> {
@@ -59,6 +61,7 @@ fn clone(&self) -> Self {
             frange: self.frange,
             source_file: self.source_file,
             should_compute_edit: self.should_compute_edit,
+            assist: self.assist.clone(),
         }
     }
 }
@@ -69,32 +72,35 @@ pub(crate) fn with_ctx<F, T>(db: &DB, frange: FileRange, should_compute_edit: bo
         F: FnOnce(AssistCtx<DB>) -> T,
     {
         let source_file = &db.parse(frange.file_id);
-        let ctx = AssistCtx {
-            db,
-            frange,
-            source_file,
-            should_compute_edit,
-        };
+        let assist =
+            if should_compute_edit { Assist::Resolved(vec![]) } else { Assist::Unresolved(vec![]) };
+
+        let ctx = AssistCtx { db, frange, source_file, should_compute_edit, assist };
         f(ctx)
     }
 
-    pub(crate) fn build(
-        self,
+    pub(crate) fn add_action(
+        &mut self,
         label: impl Into<String>,
         f: impl FnOnce(&mut AssistBuilder),
-    ) -> Option<Assist> {
-        let label = AssistLabel {
-            label: label.into(),
-        };
-        if !self.should_compute_edit {
-            return Some(Assist::Unresolved(label));
+    ) -> &mut Self {
+        let label = AssistLabel { label: label.into() };
+        match &mut self.assist {
+            Assist::Unresolved(labels) => labels.push(label),
+            Assist::Resolved(labels_actions) => {
+                let action = {
+                    let mut edit = AssistBuilder::default();
+                    f(&mut edit);
+                    edit.build()
+                };
+                labels_actions.push((label, action));
+            }
         }
-        let action = {
-            let mut edit = AssistBuilder::default();
-            f(&mut edit);
-            edit.build()
-        };
-        Some(Assist::Resolved(label, action))
+        self
+    }
+
+    pub(crate) fn build(self) -> Option<Assist> {
+        Some(self.assist)
     }
 
     pub(crate) fn leaf_at_offset(&self) -> LeafAtOffset<&'a SyntaxNode> {
@@ -113,6 +119,7 @@ pub(crate) fn covering_node(&self) -> &'a SyntaxNode {
 pub(crate) struct AssistBuilder {
     edit: TextEditBuilder,
     cursor_position: Option<TextUnit>,
+    target: Option<TextRange>,
 }
 
 impl AssistBuilder {
@@ -145,10 +152,15 @@ pub(crate) fn set_cursor(&mut self, offset: TextUnit) {
         self.cursor_position = Some(offset)
     }
 
+    pub(crate) fn target(&mut self, target: TextRange) {
+        self.target = Some(target)
+    }
+
     fn build(self) -> AssistAction {
         AssistAction {
             edit: self.edit.finish(),
             cursor_position: self.cursor_position,
+            target: self.target,
         }
     }
 }