diff options
Diffstat (limited to 'crates/ra_assists/src/assist_ctx.rs')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 71 |
1 files changed, 33 insertions, 38 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 83dd270c6..871671de2 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,8 +1,11 @@ | |||
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. |
2 | use hir::Semantics; | 2 | use hir::Semantics; |
3 | use ra_db::FileRange; | 3 | use ra_db::{FileId, FileRange}; |
4 | use ra_fmt::{leading_indent, reindent}; | 4 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::{ |
6 | source_change::{SingleFileChange, SourceChange}, | ||
7 | RootDatabase, | ||
8 | }; | ||
6 | use ra_syntax::{ | 9 | use ra_syntax::{ |
7 | algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, | 10 | algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, |
8 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, | 11 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, |
@@ -10,7 +13,7 @@ use ra_syntax::{ | |||
10 | }; | 13 | }; |
11 | use ra_text_edit::TextEditBuilder; | 14 | use ra_text_edit::TextEditBuilder; |
12 | 15 | ||
13 | use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; | 16 | use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; |
14 | 17 | ||
15 | #[derive(Clone, Debug)] | 18 | #[derive(Clone, Debug)] |
16 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); | 19 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); |
@@ -19,16 +22,16 @@ pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); | |||
19 | pub(crate) struct AssistInfo { | 22 | pub(crate) struct AssistInfo { |
20 | pub(crate) label: AssistLabel, | 23 | pub(crate) label: AssistLabel, |
21 | pub(crate) group_label: Option<GroupLabel>, | 24 | pub(crate) group_label: Option<GroupLabel>, |
22 | pub(crate) action: Option<AssistAction>, | 25 | pub(crate) source_change: Option<SourceChange>, |
23 | } | 26 | } |
24 | 27 | ||
25 | impl AssistInfo { | 28 | impl AssistInfo { |
26 | fn new(label: AssistLabel) -> AssistInfo { | 29 | fn new(label: AssistLabel) -> AssistInfo { |
27 | AssistInfo { label, group_label: None, action: None } | 30 | AssistInfo { label, group_label: None, source_change: None } |
28 | } | 31 | } |
29 | 32 | ||
30 | fn resolved(self, action: AssistAction) -> AssistInfo { | 33 | fn resolved(self, source_change: SourceChange) -> AssistInfo { |
31 | AssistInfo { action: Some(action), ..self } | 34 | AssistInfo { source_change: Some(source_change), ..self } |
32 | } | 35 | } |
33 | 36 | ||
34 | fn with_group(self, group_label: GroupLabel) -> AssistInfo { | 37 | fn with_group(self, group_label: GroupLabel) -> AssistInfo { |
@@ -37,7 +40,7 @@ impl AssistInfo { | |||
37 | 40 | ||
38 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { | 41 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { |
39 | let label = self.label; | 42 | let label = self.label; |
40 | self.action.map(|action| ResolvedAssist { label, action }) | 43 | self.source_change.map(|source_change| ResolvedAssist { label, source_change }) |
41 | } | 44 | } |
42 | } | 45 | } |
43 | 46 | ||
@@ -94,18 +97,19 @@ impl<'a> AssistCtx<'a> { | |||
94 | self, | 97 | self, |
95 | id: AssistId, | 98 | id: AssistId, |
96 | label: impl Into<String>, | 99 | label: impl Into<String>, |
100 | target: TextRange, | ||
97 | f: impl FnOnce(&mut ActionBuilder), | 101 | f: impl FnOnce(&mut ActionBuilder), |
98 | ) -> Option<Assist> { | 102 | ) -> Option<Assist> { |
99 | let label = AssistLabel::new(id, label.into(), None); | 103 | let label = AssistLabel::new(id, label.into(), None, target); |
100 | 104 | let change_label = label.label.clone(); | |
101 | let mut info = AssistInfo::new(label); | 105 | let mut info = AssistInfo::new(label); |
102 | if self.should_compute_edit { | 106 | if self.should_compute_edit { |
103 | let action = { | 107 | let source_change = { |
104 | let mut edit = ActionBuilder::new(&self); | 108 | let mut edit = ActionBuilder::new(&self); |
105 | f(&mut edit); | 109 | f(&mut edit); |
106 | edit.build() | 110 | edit.build(change_label) |
107 | }; | 111 | }; |
108 | info = info.resolved(action) | 112 | info = info.resolved(source_change) |
109 | }; | 113 | }; |
110 | 114 | ||
111 | Some(Assist(vec![info])) | 115 | Some(Assist(vec![info])) |
@@ -152,18 +156,19 @@ impl<'a> AssistGroup<'a> { | |||
152 | &mut self, | 156 | &mut self, |
153 | id: AssistId, | 157 | id: AssistId, |
154 | label: impl Into<String>, | 158 | label: impl Into<String>, |
159 | target: TextRange, | ||
155 | f: impl FnOnce(&mut ActionBuilder), | 160 | f: impl FnOnce(&mut ActionBuilder), |
156 | ) { | 161 | ) { |
157 | let label = AssistLabel::new(id, label.into(), Some(self.group.clone())); | 162 | let label = AssistLabel::new(id, label.into(), Some(self.group.clone()), target); |
158 | 163 | let change_label = label.label.clone(); | |
159 | let mut info = AssistInfo::new(label).with_group(self.group.clone()); | 164 | let mut info = AssistInfo::new(label).with_group(self.group.clone()); |
160 | if self.ctx.should_compute_edit { | 165 | if self.ctx.should_compute_edit { |
161 | let action = { | 166 | let source_change = { |
162 | let mut edit = ActionBuilder::new(&self.ctx); | 167 | let mut edit = ActionBuilder::new(&self.ctx); |
163 | f(&mut edit); | 168 | f(&mut edit); |
164 | edit.build() | 169 | edit.build(change_label) |
165 | }; | 170 | }; |
166 | info = info.resolved(action) | 171 | info = info.resolved(source_change) |
167 | }; | 172 | }; |
168 | 173 | ||
169 | self.assists.push(info) | 174 | self.assists.push(info) |
@@ -181,8 +186,7 @@ impl<'a> AssistGroup<'a> { | |||
181 | pub(crate) struct ActionBuilder<'a, 'b> { | 186 | pub(crate) struct ActionBuilder<'a, 'b> { |
182 | edit: TextEditBuilder, | 187 | edit: TextEditBuilder, |
183 | cursor_position: Option<TextSize>, | 188 | cursor_position: Option<TextSize>, |
184 | target: Option<TextRange>, | 189 | file: FileId, |
185 | file: AssistFile, | ||
186 | ctx: &'a AssistCtx<'b>, | 190 | ctx: &'a AssistCtx<'b>, |
187 | } | 191 | } |
188 | 192 | ||
@@ -191,8 +195,7 @@ impl<'a, 'b> ActionBuilder<'a, 'b> { | |||
191 | Self { | 195 | Self { |
192 | edit: TextEditBuilder::default(), | 196 | edit: TextEditBuilder::default(), |
193 | cursor_position: None, | 197 | cursor_position: None, |
194 | target: None, | 198 | file: ctx.frange.file_id, |
195 | file: AssistFile::default(), | ||
196 | ctx, | 199 | ctx, |
197 | } | 200 | } |
198 | } | 201 | } |
@@ -237,14 +240,6 @@ impl<'a, 'b> ActionBuilder<'a, 'b> { | |||
237 | self.cursor_position = Some(offset) | 240 | self.cursor_position = Some(offset) |
238 | } | 241 | } |
239 | 242 | ||
240 | /// Specify that the assist should be active withing the `target` range. | ||
241 | /// | ||
242 | /// Target ranges are used to sort assists: the smaller the target range, | ||
243 | /// the more specific assist is, and so it should be sorted first. | ||
244 | pub(crate) fn target(&mut self, target: TextRange) { | ||
245 | self.target = Some(target) | ||
246 | } | ||
247 | |||
248 | /// Get access to the raw `TextEditBuilder`. | 243 | /// Get access to the raw `TextEditBuilder`. |
249 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | 244 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { |
250 | &mut self.edit | 245 | &mut self.edit |
@@ -259,16 +254,16 @@ impl<'a, 'b> ActionBuilder<'a, 'b> { | |||
259 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 254 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
260 | } | 255 | } |
261 | 256 | ||
262 | pub(crate) fn set_file(&mut self, assist_file: AssistFile) { | 257 | pub(crate) fn set_file(&mut self, assist_file: FileId) { |
263 | self.file = assist_file | 258 | self.file = assist_file; |
264 | } | 259 | } |
265 | 260 | ||
266 | fn build(self) -> AssistAction { | 261 | fn build(self, change_label: String) -> SourceChange { |
267 | AssistAction { | 262 | let edit = self.edit.finish(); |
268 | edit: self.edit.finish(), | 263 | if edit.is_empty() && self.cursor_position.is_none() { |
269 | cursor_position: self.cursor_position, | 264 | panic!("Only call `add_assist` if the assist can be applied") |
270 | target: self.target, | ||
271 | file: self.file, | ||
272 | } | 265 | } |
266 | SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } | ||
267 | .into_source_change(self.file) | ||
273 | } | 268 | } |
274 | } | 269 | } |