diff options
Diffstat (limited to 'crates/ra_assists/src/assist_context.rs')
-rw-r--r-- | crates/ra_assists/src/assist_context.rs | 58 |
1 files changed, 41 insertions, 17 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index a680f752b..5b1a4680b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -5,7 +5,7 @@ use hir::Semantics; | |||
5 | use ra_db::{FileId, FileRange}; | 5 | use ra_db::{FileId, FileRange}; |
6 | use ra_fmt::{leading_indent, reindent}; | 6 | use ra_fmt::{leading_indent, reindent}; |
7 | use ra_ide_db::{ | 7 | use ra_ide_db::{ |
8 | source_change::{SingleFileChange, SourceChange}, | 8 | source_change::{SourceChange, SourceFileEdit}, |
9 | RootDatabase, | 9 | RootDatabase, |
10 | }; | 10 | }; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{ |
@@ -15,7 +15,10 @@ use ra_syntax::{ | |||
15 | }; | 15 | }; |
16 | use ra_text_edit::TextEditBuilder; | 16 | use ra_text_edit::TextEditBuilder; |
17 | 17 | ||
18 | use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | 18 | use crate::{ |
19 | assist_config::{AssistConfig, SnippetCap}, | ||
20 | Assist, AssistId, GroupLabel, ResolvedAssist, | ||
21 | }; | ||
19 | 22 | ||
20 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 23 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
21 | /// | 24 | /// |
@@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | |||
48 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 51 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
49 | /// easier to just compute the edit eagerly :-) | 52 | /// easier to just compute the edit eagerly :-) |
50 | pub(crate) struct AssistContext<'a> { | 53 | pub(crate) struct AssistContext<'a> { |
54 | pub(crate) config: &'a AssistConfig, | ||
51 | pub(crate) sema: Semantics<'a, RootDatabase>, | 55 | pub(crate) sema: Semantics<'a, RootDatabase>, |
52 | pub(crate) db: &'a RootDatabase, | 56 | pub(crate) db: &'a RootDatabase, |
53 | pub(crate) frange: FileRange, | 57 | pub(crate) frange: FileRange, |
@@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> { | |||
55 | } | 59 | } |
56 | 60 | ||
57 | impl<'a> AssistContext<'a> { | 61 | impl<'a> AssistContext<'a> { |
58 | pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { | 62 | pub(crate) fn new( |
63 | sema: Semantics<'a, RootDatabase>, | ||
64 | config: &'a AssistConfig, | ||
65 | frange: FileRange, | ||
66 | ) -> AssistContext<'a> { | ||
59 | let source_file = sema.parse(frange.file_id); | 67 | let source_file = sema.parse(frange.file_id); |
60 | let db = sema.db; | 68 | let db = sema.db; |
61 | AssistContext { sema, db, frange, source_file } | 69 | AssistContext { config, sema, db, frange, source_file } |
62 | } | 70 | } |
63 | 71 | ||
64 | // NB, this ignores active selection. | 72 | // NB, this ignores active selection. |
@@ -142,11 +150,10 @@ impl Assists { | |||
142 | self.add_impl(label, f) | 150 | self.add_impl(label, f) |
143 | } | 151 | } |
144 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 152 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
145 | let change_label = label.label.clone(); | ||
146 | let source_change = if self.resolve { | 153 | let source_change = if self.resolve { |
147 | let mut builder = AssistBuilder::new(self.file); | 154 | let mut builder = AssistBuilder::new(self.file); |
148 | f(&mut builder); | 155 | f(&mut builder); |
149 | Some(builder.finish(change_label)) | 156 | Some(builder.finish()) |
150 | } else { | 157 | } else { |
151 | None | 158 | None |
152 | }; | 159 | }; |
@@ -163,13 +170,13 @@ impl Assists { | |||
163 | 170 | ||
164 | pub(crate) struct AssistBuilder { | 171 | pub(crate) struct AssistBuilder { |
165 | edit: TextEditBuilder, | 172 | edit: TextEditBuilder, |
166 | cursor_position: Option<TextSize>, | ||
167 | file: FileId, | 173 | file: FileId, |
174 | is_snippet: bool, | ||
168 | } | 175 | } |
169 | 176 | ||
170 | impl AssistBuilder { | 177 | impl AssistBuilder { |
171 | pub(crate) fn new(file: FileId) -> AssistBuilder { | 178 | pub(crate) fn new(file: FileId) -> AssistBuilder { |
172 | AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } | 179 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } |
173 | } | 180 | } |
174 | 181 | ||
175 | /// Remove specified `range` of text. | 182 | /// Remove specified `range` of text. |
@@ -180,10 +187,30 @@ impl AssistBuilder { | |||
180 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | 187 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { |
181 | self.edit.insert(offset, text.into()) | 188 | self.edit.insert(offset, text.into()) |
182 | } | 189 | } |
190 | /// Append specified `snippet` at the given `offset` | ||
191 | pub(crate) fn insert_snippet( | ||
192 | &mut self, | ||
193 | _cap: SnippetCap, | ||
194 | offset: TextSize, | ||
195 | snippet: impl Into<String>, | ||
196 | ) { | ||
197 | self.is_snippet = true; | ||
198 | self.insert(offset, snippet); | ||
199 | } | ||
183 | /// Replaces specified `range` of text with a given string. | 200 | /// Replaces specified `range` of text with a given string. |
184 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | 201 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { |
185 | self.edit.replace(range, replace_with.into()) | 202 | self.edit.replace(range, replace_with.into()) |
186 | } | 203 | } |
204 | /// Replaces specified `range` of text with a given `snippet`. | ||
205 | pub(crate) fn replace_snippet( | ||
206 | &mut self, | ||
207 | _cap: SnippetCap, | ||
208 | range: TextRange, | ||
209 | snippet: impl Into<String>, | ||
210 | ) { | ||
211 | self.is_snippet = true; | ||
212 | self.replace(range, snippet); | ||
213 | } | ||
187 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | 214 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { |
188 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 215 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
189 | } | 216 | } |
@@ -207,10 +234,6 @@ impl AssistBuilder { | |||
207 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 234 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
208 | } | 235 | } |
209 | 236 | ||
210 | /// Specify desired position of the cursor after the assist is applied. | ||
211 | pub(crate) fn set_cursor(&mut self, offset: TextSize) { | ||
212 | self.cursor_position = Some(offset) | ||
213 | } | ||
214 | // FIXME: better API | 237 | // FIXME: better API |
215 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | 238 | pub(crate) fn set_file(&mut self, assist_file: FileId) { |
216 | self.file = assist_file; | 239 | self.file = assist_file; |
@@ -222,12 +245,13 @@ impl AssistBuilder { | |||
222 | &mut self.edit | 245 | &mut self.edit |
223 | } | 246 | } |
224 | 247 | ||
225 | fn finish(self, change_label: String) -> SourceChange { | 248 | fn finish(self) -> SourceChange { |
226 | let edit = self.edit.finish(); | 249 | let edit = self.edit.finish(); |
227 | if edit.is_empty() && self.cursor_position.is_none() { | 250 | let source_file_edit = SourceFileEdit { file_id: self.file, edit }; |
228 | panic!("Only call `add_assist` if the assist can be applied") | 251 | let mut res: SourceChange = source_file_edit.into(); |
252 | if self.is_snippet { | ||
253 | res.is_snippet = true; | ||
229 | } | 254 | } |
230 | SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } | 255 | res |
231 | .into_source_change(self.file) | ||
232 | } | 256 | } |
233 | } | 257 | } |