aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assist_context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assist_context.rs')
-rw-r--r--crates/ra_assists/src/assist_context.rs87
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
3use std::mem;
4
3use algo::find_covering_element; 5use algo::find_covering_element;
4use hir::Semantics; 6use hir::Semantics;
5use ra_db::{FileId, FileRange}; 7use ra_db::{FileId, FileRange};
@@ -17,7 +19,7 @@ use ra_text_edit::TextEditBuilder;
17 19
18use crate::{ 20use 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::{
53pub(crate) struct AssistContext<'a> { 55pub(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
104impl Assists { 109impl 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
171pub(crate) struct AssistBuilder { 203pub(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
177impl AssistBuilder { 210impl 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}