aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assist_ctx.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assist_ctx.rs')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs60
1 files changed, 51 insertions, 9 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 28152f724..43f0d664b 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -1,5 +1,6 @@
1//! This module defines `AssistCtx` -- the API surface that is exposed to assists. 1//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2use hir::{db::HirDatabase, InFile, SourceAnalyzer}; 2use either::Either;
3use hir::{db::HirDatabase, InFile, SourceAnalyzer, SourceBinder};
3use ra_db::FileRange; 4use ra_db::FileRange;
4use ra_fmt::{leading_indent, reindent}; 5use ra_fmt::{leading_indent, reindent};
5use ra_syntax::{ 6use ra_syntax::{
@@ -9,12 +10,12 @@ use ra_syntax::{
9}; 10};
10use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
11 12
12use crate::{AssistAction, AssistId, AssistLabel}; 13use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist};
13 14
14#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
15pub(crate) enum Assist { 16pub(crate) enum Assist {
16 Unresolved { label: AssistLabel }, 17 Unresolved { label: AssistLabel },
17 Resolved { label: AssistLabel, action: AssistAction }, 18 Resolved { assist: ResolvedAssist },
18} 19}
19 20
20/// `AssistCtx` allows to apply an assist or check if it could be applied. 21/// `AssistCtx` allows to apply an assist or check if it could be applied.
@@ -81,16 +82,45 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
81 self, 82 self,
82 id: AssistId, 83 id: AssistId,
83 label: impl Into<String>, 84 label: impl Into<String>,
84 f: impl FnOnce(&mut AssistBuilder), 85 f: impl FnOnce(&mut ActionBuilder),
85 ) -> Option<Assist> { 86 ) -> Option<Assist> {
86 let label = AssistLabel { label: label.into(), id }; 87 let label = AssistLabel { label: label.into(), id };
88 assert!(label.label.chars().nth(0).unwrap().is_uppercase());
89
87 let assist = if self.should_compute_edit { 90 let assist = if self.should_compute_edit {
88 let action = { 91 let action = {
89 let mut edit = AssistBuilder::default(); 92 let mut edit = ActionBuilder::default();
90 f(&mut edit); 93 f(&mut edit);
91 edit.build() 94 edit.build()
92 }; 95 };
93 Assist::Resolved { label, action } 96 Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } }
97 } else {
98 Assist::Unresolved { label }
99 };
100
101 Some(assist)
102 }
103
104 #[allow(dead_code)] // will be used for auto import assist with multiple actions
105 pub(crate) fn add_assist_group(
106 self,
107 id: AssistId,
108 label: impl Into<String>,
109 f: impl FnOnce() -> Vec<ActionBuilder>,
110 ) -> Option<Assist> {
111 let label = AssistLabel { label: label.into(), id };
112 let assist = if self.should_compute_edit {
113 let actions = f();
114 assert!(!actions.is_empty(), "Assist cannot have no");
115
116 Assist::Resolved {
117 assist: ResolvedAssist {
118 label,
119 action_data: Either::Right(
120 actions.into_iter().map(ActionBuilder::build).collect(),
121 ),
122 },
123 }
94 } else { 124 } else {
95 Assist::Unresolved { label } 125 Assist::Unresolved { label }
96 }; 126 };
@@ -112,12 +142,16 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
112 pub(crate) fn covering_element(&self) -> SyntaxElement { 142 pub(crate) fn covering_element(&self) -> SyntaxElement {
113 find_covering_element(self.source_file.syntax(), self.frange.range) 143 find_covering_element(self.source_file.syntax(), self.frange.range)
114 } 144 }
145 pub(crate) fn source_binder(&self) -> SourceBinder<'a, DB> {
146 SourceBinder::new(self.db)
147 }
115 pub(crate) fn source_analyzer( 148 pub(crate) fn source_analyzer(
116 &self, 149 &self,
117 node: &SyntaxNode, 150 node: &SyntaxNode,
118 offset: Option<TextUnit>, 151 offset: Option<TextUnit>,
119 ) -> SourceAnalyzer { 152 ) -> SourceAnalyzer {
120 SourceAnalyzer::new(self.db, InFile::new(self.frange.file_id.into(), node), offset) 153 let src = InFile::new(self.frange.file_id.into(), node);
154 self.source_binder().analyze(src, offset)
121 } 155 }
122 156
123 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { 157 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
@@ -126,13 +160,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
126} 160}
127 161
128#[derive(Default)] 162#[derive(Default)]
129pub(crate) struct AssistBuilder { 163pub(crate) struct ActionBuilder {
130 edit: TextEditBuilder, 164 edit: TextEditBuilder,
131 cursor_position: Option<TextUnit>, 165 cursor_position: Option<TextUnit>,
132 target: Option<TextRange>, 166 target: Option<TextRange>,
167 label: Option<String>,
133} 168}
134 169
135impl AssistBuilder { 170impl ActionBuilder {
171 #[allow(dead_code)]
172 /// Adds a custom label to the action, if it needs to be different from the assist label
173 pub(crate) fn label(&mut self, label: impl Into<String>) {
174 self.label = Some(label.into())
175 }
176
136 /// Replaces specified `range` of text with a given string. 177 /// Replaces specified `range` of text with a given string.
137 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { 178 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
138 self.edit.replace(range, replace_with.into()) 179 self.edit.replace(range, replace_with.into())
@@ -191,6 +232,7 @@ impl AssistBuilder {
191 edit: self.edit.finish(), 232 edit: self.edit.finish(),
192 cursor_position: self.cursor_position, 233 cursor_position: self.cursor_position,
193 target: self.target, 234 target: self.target,
235 label: self.label,
194 } 236 }
195 } 237 }
196} 238}