diff options
Diffstat (limited to 'crates/ra_assists/src/assist_context.rs')
-rw-r--r-- | crates/ra_assists/src/assist_context.rs | 87 |
1 files changed, 66 insertions, 21 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 5b1a4680b..3407df856 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! See `AssistContext` | 1 | //! See `AssistContext` |
2 | 2 | ||
3 | use std::mem; | ||
4 | |||
3 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
4 | use hir::Semantics; | 6 | use hir::Semantics; |
5 | use ra_db::{FileId, FileRange}; | 7 | use ra_db::{FileId, FileRange}; |
@@ -17,7 +19,7 @@ use ra_text_edit::TextEditBuilder; | |||
17 | 19 | ||
18 | use crate::{ | 20 | use crate::{ |
19 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
20 | Assist, AssistId, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, |
21 | }; | 23 | }; |
22 | 24 | ||
23 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 25 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
@@ -53,7 +55,6 @@ use crate::{ | |||
53 | pub(crate) struct AssistContext<'a> { | 55 | pub(crate) struct AssistContext<'a> { |
54 | pub(crate) config: &'a AssistConfig, | 56 | pub(crate) config: &'a AssistConfig, |
55 | pub(crate) sema: Semantics<'a, RootDatabase>, | 57 | pub(crate) sema: Semantics<'a, RootDatabase>, |
56 | pub(crate) db: &'a RootDatabase, | ||
57 | pub(crate) frange: FileRange, | 58 | pub(crate) frange: FileRange, |
58 | source_file: SourceFile, | 59 | source_file: SourceFile, |
59 | } | 60 | } |
@@ -65,8 +66,11 @@ impl<'a> AssistContext<'a> { | |||
65 | frange: FileRange, | 66 | frange: FileRange, |
66 | ) -> AssistContext<'a> { | 67 | ) -> AssistContext<'a> { |
67 | let source_file = sema.parse(frange.file_id); | 68 | let source_file = sema.parse(frange.file_id); |
68 | let db = sema.db; | 69 | AssistContext { config, sema, frange, source_file } |
69 | AssistContext { config, sema, db, frange, source_file } | 70 | } |
71 | |||
72 | pub(crate) fn db(&self) -> &RootDatabase { | ||
73 | self.sema.db | ||
70 | } | 74 | } |
71 | 75 | ||
72 | // NB, this ignores active selection. | 76 | // NB, this ignores active selection. |
@@ -99,14 +103,26 @@ pub(crate) struct Assists { | |||
99 | resolve: bool, | 103 | resolve: bool, |
100 | file: FileId, | 104 | file: FileId, |
101 | buf: Vec<(Assist, Option<SourceChange>)>, | 105 | buf: Vec<(Assist, Option<SourceChange>)>, |
106 | allowed: Option<Vec<AssistKind>>, | ||
102 | } | 107 | } |
103 | 108 | ||
104 | impl Assists { | 109 | impl Assists { |
105 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 110 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { |
106 | Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } | 111 | Assists { |
112 | resolve: true, | ||
113 | file: ctx.frange.file_id, | ||
114 | buf: Vec::new(), | ||
115 | allowed: ctx.config.allowed.clone(), | ||
116 | } | ||
107 | } | 117 | } |
118 | |||
108 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 119 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { |
109 | Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } | 120 | Assists { |
121 | resolve: false, | ||
122 | file: ctx.frange.file_id, | ||
123 | buf: Vec::new(), | ||
124 | allowed: ctx.config.allowed.clone(), | ||
125 | } | ||
110 | } | 126 | } |
111 | 127 | ||
112 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | 128 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { |
@@ -135,9 +151,13 @@ impl Assists { | |||
135 | target: TextRange, | 151 | target: TextRange, |
136 | f: impl FnOnce(&mut AssistBuilder), | 152 | f: impl FnOnce(&mut AssistBuilder), |
137 | ) -> Option<()> { | 153 | ) -> Option<()> { |
154 | if !self.is_allowed(&id) { | ||
155 | return None; | ||
156 | } | ||
138 | let label = Assist::new(id, label.into(), None, target); | 157 | let label = Assist::new(id, label.into(), None, target); |
139 | self.add_impl(label, f) | 158 | self.add_impl(label, f) |
140 | } | 159 | } |
160 | |||
141 | pub(crate) fn add_group( | 161 | pub(crate) fn add_group( |
142 | &mut self, | 162 | &mut self, |
143 | group: &GroupLabel, | 163 | group: &GroupLabel, |
@@ -146,9 +166,14 @@ impl Assists { | |||
146 | target: TextRange, | 166 | target: TextRange, |
147 | f: impl FnOnce(&mut AssistBuilder), | 167 | f: impl FnOnce(&mut AssistBuilder), |
148 | ) -> Option<()> { | 168 | ) -> Option<()> { |
169 | if !self.is_allowed(&id) { | ||
170 | return None; | ||
171 | } | ||
172 | |||
149 | let label = Assist::new(id, label.into(), Some(group.clone()), target); | 173 | let label = Assist::new(id, label.into(), Some(group.clone()), target); |
150 | self.add_impl(label, f) | 174 | self.add_impl(label, f) |
151 | } | 175 | } |
176 | |||
152 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 177 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
153 | let source_change = if self.resolve { | 178 | let source_change = if self.resolve { |
154 | let mut builder = AssistBuilder::new(self.file); | 179 | let mut builder = AssistBuilder::new(self.file); |
@@ -166,17 +191,43 @@ impl Assists { | |||
166 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | 191 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); |
167 | self.buf | 192 | self.buf |
168 | } | 193 | } |
194 | |||
195 | fn is_allowed(&self, id: &AssistId) -> bool { | ||
196 | match &self.allowed { | ||
197 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), | ||
198 | None => true, | ||
199 | } | ||
200 | } | ||
169 | } | 201 | } |
170 | 202 | ||
171 | pub(crate) struct AssistBuilder { | 203 | pub(crate) struct AssistBuilder { |
172 | edit: TextEditBuilder, | 204 | edit: TextEditBuilder, |
173 | file: FileId, | 205 | file_id: FileId, |
174 | is_snippet: bool, | 206 | is_snippet: bool, |
207 | change: SourceChange, | ||
175 | } | 208 | } |
176 | 209 | ||
177 | impl AssistBuilder { | 210 | impl AssistBuilder { |
178 | pub(crate) fn new(file: FileId) -> AssistBuilder { | 211 | pub(crate) fn new(file_id: FileId) -> AssistBuilder { |
179 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } | 212 | AssistBuilder { |
213 | edit: TextEditBuilder::default(), | ||
214 | file_id, | ||
215 | is_snippet: false, | ||
216 | change: SourceChange::default(), | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pub(crate) fn edit_file(&mut self, file_id: FileId) { | ||
221 | self.file_id = file_id; | ||
222 | } | ||
223 | |||
224 | fn commit(&mut self) { | ||
225 | let edit = mem::take(&mut self.edit).finish(); | ||
226 | if !edit.is_empty() { | ||
227 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; | ||
228 | assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id)); | ||
229 | self.change.source_file_edits.push(new_edit); | ||
230 | } | ||
180 | } | 231 | } |
181 | 232 | ||
182 | /// Remove specified `range` of text. | 233 | /// Remove specified `range` of text. |
@@ -231,12 +282,7 @@ impl AssistBuilder { | |||
231 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | 282 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { |
232 | let node = rewriter.rewrite_root().unwrap(); | 283 | let node = rewriter.rewrite_root().unwrap(); |
233 | let new = rewriter.rewrite(&node); | 284 | let new = rewriter.rewrite(&node); |
234 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 285 | algo::diff(&node, &new).into_text_edit(&mut self.edit); |
235 | } | ||
236 | |||
237 | // FIXME: better API | ||
238 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
239 | self.file = assist_file; | ||
240 | } | 286 | } |
241 | 287 | ||
242 | // FIXME: kill this API | 288 | // FIXME: kill this API |
@@ -245,13 +291,12 @@ impl AssistBuilder { | |||
245 | &mut self.edit | 291 | &mut self.edit |
246 | } | 292 | } |
247 | 293 | ||
248 | fn finish(self) -> SourceChange { | 294 | fn finish(mut self) -> SourceChange { |
249 | let edit = self.edit.finish(); | 295 | self.commit(); |
250 | let source_file_edit = SourceFileEdit { file_id: self.file, edit }; | 296 | let mut change = mem::take(&mut self.change); |
251 | let mut res: SourceChange = source_file_edit.into(); | ||
252 | if self.is_snippet { | 297 | if self.is_snippet { |
253 | res.is_snippet = true; | 298 | change.is_snippet = true; |
254 | } | 299 | } |
255 | res | 300 | change |
256 | } | 301 | } |
257 | } | 302 | } |