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.rs71
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.
2use hir::Semantics; 2use hir::Semantics;
3use ra_db::FileRange; 3use ra_db::{FileId, FileRange};
4use ra_fmt::{leading_indent, reindent}; 4use ra_fmt::{leading_indent, reindent};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{
6 source_change::{SingleFileChange, SourceChange},
7 RootDatabase,
8};
6use ra_syntax::{ 9use 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};
11use ra_text_edit::TextEditBuilder; 14use ra_text_edit::TextEditBuilder;
12 15
13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 16use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14 17
15#[derive(Clone, Debug)] 18#[derive(Clone, Debug)]
16pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); 19pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
@@ -19,16 +22,16 @@ pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
19pub(crate) struct AssistInfo { 22pub(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
25impl AssistInfo { 28impl 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> {
181pub(crate) struct ActionBuilder<'a, 'b> { 186pub(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}