aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
authorkjeremy <[email protected]>2020-07-13 22:41:47 +0100
committerkjeremy <[email protected]>2020-07-13 22:41:47 +0100
commitaa598ecb75331068f4cacd443512991bc48937dc (patch)
tree785b88b46016507ed2ced5befce6885780664ff2 /crates/ra_assists
parent853440775d72974585ca3fe39f9688a4d4302dd3 (diff)
Filter assists
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_context.rs38
-rw-r--r--crates/ra_assists/src/lib.rs31
-rw-r--r--crates/ra_assists/src/tests.rs52
3 files changed, 108 insertions, 13 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index c33525363..9ca2cfe68 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -19,7 +19,7 @@ use ra_text_edit::TextEditBuilder;
19 19
20use crate::{ 20use crate::{
21 assist_config::{AssistConfig, SnippetCap}, 21 assist_config::{AssistConfig, SnippetCap},
22 Assist, AssistId, GroupLabel, ResolvedAssist, 22 Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
23}; 23};
24 24
25/// `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.
@@ -57,6 +57,7 @@ pub(crate) struct AssistContext<'a> {
57 pub(crate) sema: Semantics<'a, RootDatabase>, 57 pub(crate) sema: Semantics<'a, RootDatabase>,
58 pub(crate) frange: FileRange, 58 pub(crate) frange: FileRange,
59 source_file: SourceFile, 59 source_file: SourceFile,
60 allowed: Option<Vec<AssistKind>>,
60} 61}
61 62
62impl<'a> AssistContext<'a> { 63impl<'a> AssistContext<'a> {
@@ -64,9 +65,10 @@ impl<'a> AssistContext<'a> {
64 sema: Semantics<'a, RootDatabase>, 65 sema: Semantics<'a, RootDatabase>,
65 config: &'a AssistConfig, 66 config: &'a AssistConfig,
66 frange: FileRange, 67 frange: FileRange,
68 allowed: Option<Vec<AssistKind>>,
67 ) -> AssistContext<'a> { 69 ) -> AssistContext<'a> {
68 let source_file = sema.parse(frange.file_id); 70 let source_file = sema.parse(frange.file_id);
69 AssistContext { config, sema, frange, source_file } 71 AssistContext { config, sema, frange, source_file, allowed }
70 } 72 }
71 73
72 pub(crate) fn db(&self) -> &RootDatabase { 74 pub(crate) fn db(&self) -> &RootDatabase {
@@ -103,14 +105,26 @@ pub(crate) struct Assists {
103 resolve: bool, 105 resolve: bool,
104 file: FileId, 106 file: FileId,
105 buf: Vec<(Assist, Option<SourceChange>)>, 107 buf: Vec<(Assist, Option<SourceChange>)>,
108 allowed: Option<Vec<AssistKind>>,
106} 109}
107 110
108impl Assists { 111impl Assists {
109 pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { 112 pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
110 Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } 113 Assists {
114 resolve: true,
115 file: ctx.frange.file_id,
116 buf: Vec::new(),
117 allowed: ctx.allowed.clone(),
118 }
111 } 119 }
120
112 pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { 121 pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
113 Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } 122 Assists {
123 resolve: false,
124 file: ctx.frange.file_id,
125 buf: Vec::new(),
126 allowed: ctx.allowed.clone(),
127 }
114 } 128 }
115 129
116 pub(crate) fn finish_unresolved(self) -> Vec<Assist> { 130 pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
@@ -139,9 +153,13 @@ impl Assists {
139 target: TextRange, 153 target: TextRange,
140 f: impl FnOnce(&mut AssistBuilder), 154 f: impl FnOnce(&mut AssistBuilder),
141 ) -> Option<()> { 155 ) -> Option<()> {
156 if !self.is_allowed(&id) {
157 return None;
158 }
142 let label = Assist::new(id, label.into(), None, target); 159 let label = Assist::new(id, label.into(), None, target);
143 self.add_impl(label, f) 160 self.add_impl(label, f)
144 } 161 }
162
145 pub(crate) fn add_group( 163 pub(crate) fn add_group(
146 &mut self, 164 &mut self,
147 group: &GroupLabel, 165 group: &GroupLabel,
@@ -150,9 +168,14 @@ impl Assists {
150 target: TextRange, 168 target: TextRange,
151 f: impl FnOnce(&mut AssistBuilder), 169 f: impl FnOnce(&mut AssistBuilder),
152 ) -> Option<()> { 170 ) -> Option<()> {
171 if !self.is_allowed(&id) {
172 return None;
173 }
174
153 let label = Assist::new(id, label.into(), Some(group.clone()), target); 175 let label = Assist::new(id, label.into(), Some(group.clone()), target);
154 self.add_impl(label, f) 176 self.add_impl(label, f)
155 } 177 }
178
156 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 179 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
157 let source_change = if self.resolve { 180 let source_change = if self.resolve {
158 let mut builder = AssistBuilder::new(self.file); 181 let mut builder = AssistBuilder::new(self.file);
@@ -170,6 +193,13 @@ impl Assists {
170 self.buf.sort_by_key(|(label, _edit)| label.target.len()); 193 self.buf.sort_by_key(|(label, _edit)| label.target.len());
171 self.buf 194 self.buf
172 } 195 }
196
197 fn is_allowed(&self, id: &AssistId) -> bool {
198 match &self.allowed {
199 Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),
200 None => true,
201 }
202 }
173} 203}
174 204
175pub(crate) struct AssistBuilder { 205pub(crate) struct AssistBuilder {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3d61fbded..13a283760 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -37,6 +37,25 @@ pub enum AssistKind {
37 RefactorRewrite, 37 RefactorRewrite,
38} 38}
39 39
40impl AssistKind {
41 pub fn contains(self, other: AssistKind) -> bool {
42 if self == other {
43 return true;
44 }
45
46 match self {
47 AssistKind::None | AssistKind::Generate => return true,
48 AssistKind::Refactor => match other {
49 AssistKind::RefactorExtract
50 | AssistKind::RefactorInline
51 | AssistKind::RefactorRewrite => return true,
52 _ => return false,
53 },
54 _ => return false,
55 }
56 }
57}
58
40/// Unique identifier of the assist, should not be shown to the user 59/// Unique identifier of the assist, should not be shown to the user
41/// directly. 60/// directly.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)] 61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -67,9 +86,14 @@ impl Assist {
67 /// 86 ///
68 /// Assists are returned in the "unresolved" state, that is only labels are 87 /// Assists are returned in the "unresolved" state, that is only labels are
69 /// returned, without actual edits. 88 /// returned, without actual edits.
70 pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> { 89 pub fn unresolved(
90 db: &RootDatabase,
91 config: &AssistConfig,
92 range: FileRange,
93 allowed: Option<Vec<AssistKind>>,
94 ) -> Vec<Assist> {
71 let sema = Semantics::new(db); 95 let sema = Semantics::new(db);
72 let ctx = AssistContext::new(sema, config, range); 96 let ctx = AssistContext::new(sema, config, range, allowed);
73 let mut acc = Assists::new_unresolved(&ctx); 97 let mut acc = Assists::new_unresolved(&ctx);
74 handlers::all().iter().for_each(|handler| { 98 handlers::all().iter().for_each(|handler| {
75 handler(&mut acc, &ctx); 99 handler(&mut acc, &ctx);
@@ -85,9 +109,10 @@ impl Assist {
85 db: &RootDatabase, 109 db: &RootDatabase,
86 config: &AssistConfig, 110 config: &AssistConfig,
87 range: FileRange, 111 range: FileRange,
112 allowed: Option<Vec<AssistKind>>,
88 ) -> Vec<ResolvedAssist> { 113 ) -> Vec<ResolvedAssist> {
89 let sema = Semantics::new(db); 114 let sema = Semantics::new(db);
90 let ctx = AssistContext::new(sema, config, range); 115 let ctx = AssistContext::new(sema, config, range, allowed);
91 let mut acc = Assists::new_resolved(&ctx); 116 let mut acc = Assists::new_resolved(&ctx);
92 handlers::all().iter().for_each(|handler| { 117 handlers::all().iter().for_each(|handler| {
93 handler(&mut acc, &ctx); 118 handler(&mut acc, &ctx);
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 858f5ca80..861622d86 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -6,7 +6,7 @@ use ra_ide_db::RootDatabase;
6use ra_syntax::TextRange; 6use ra_syntax::TextRange;
7use test_utils::{assert_eq_text, extract_offset, extract_range}; 7use test_utils::{assert_eq_text, extract_offset, extract_range};
8 8
9use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; 9use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
10use stdx::trim_indent; 10use stdx::trim_indent;
11 11
12pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 12pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
@@ -35,14 +35,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
35 let before = db.file_text(file_id).to_string(); 35 let before = db.file_text(file_id).to_string();
36 let frange = FileRange { file_id, range: selection.into() }; 36 let frange = FileRange { file_id, range: selection.into() };
37 37
38 let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) 38 let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange, None)
39 .into_iter() 39 .into_iter()
40 .find(|assist| assist.assist.id.0 == assist_id) 40 .find(|assist| assist.assist.id.0 == assist_id)
41 .unwrap_or_else(|| { 41 .unwrap_or_else(|| {
42 panic!( 42 panic!(
43 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 43 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
44 assist_id, 44 assist_id,
45 Assist::resolved(&db, &AssistConfig::default(), frange) 45 Assist::resolved(&db, &AssistConfig::default(), frange, None)
46 .into_iter() 46 .into_iter()
47 .map(|assist| assist.assist.id.0) 47 .map(|assist| assist.assist.id.0)
48 .collect::<Vec<_>>() 48 .collect::<Vec<_>>()
@@ -73,7 +73,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
73 73
74 let sema = Semantics::new(&db); 74 let sema = Semantics::new(&db);
75 let config = AssistConfig::default(); 75 let config = AssistConfig::default();
76 let ctx = AssistContext::new(sema, &config, frange); 76 let ctx = AssistContext::new(sema, &config, frange, None);
77 let mut acc = Assists::new_resolved(&ctx); 77 let mut acc = Assists::new_resolved(&ctx);
78 handler(&mut acc, &ctx); 78 handler(&mut acc, &ctx);
79 let mut res = acc.finish_resolved(); 79 let mut res = acc.finish_resolved();
@@ -105,7 +105,7 @@ fn assist_order_field_struct() {
105 let (before_cursor_pos, before) = extract_offset(before); 105 let (before_cursor_pos, before) = extract_offset(before);
106 let (db, file_id) = with_single_file(&before); 106 let (db, file_id) = with_single_file(&before);
107 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 107 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
108 let assists = Assist::resolved(&db, &AssistConfig::default(), frange); 108 let assists = Assist::resolved(&db, &AssistConfig::default(), frange, None);
109 let mut assists = assists.iter(); 109 let mut assists = assists.iter();
110 110
111 assert_eq!( 111 assert_eq!(
@@ -128,9 +128,49 @@ fn assist_order_if_expr() {
128 let (range, before) = extract_range(before); 128 let (range, before) = extract_range(before);
129 let (db, file_id) = with_single_file(&before); 129 let (db, file_id) = with_single_file(&before);
130 let frange = FileRange { file_id, range }; 130 let frange = FileRange { file_id, range };
131 let assists = Assist::resolved(&db, &AssistConfig::default(), frange); 131 let assists = Assist::resolved(&db, &AssistConfig::default(), frange, None);
132 let mut assists = assists.iter(); 132 let mut assists = assists.iter();
133 133
134 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 134 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
135 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); 135 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
136} 136}
137
138#[test]
139fn assist_filter_works() {
140 let before = "
141 pub fn test_some_range(a: int) -> bool {
142 if let 2..6 = <|>5<|> {
143 true
144 } else {
145 false
146 }
147 }";
148 let (range, before) = extract_range(before);
149 let (db, file_id) = with_single_file(&before);
150 let frange = FileRange { file_id, range };
151
152 {
153 let allowed = Some(vec![AssistKind::Refactor]);
154
155 let assists = Assist::resolved(&db, &AssistConfig::default(), frange, allowed);
156 let mut assists = assists.iter();
157
158 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
159 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
160 }
161
162 {
163 let allowed = Some(vec![AssistKind::RefactorExtract]);
164 let assists = Assist::resolved(&db, &AssistConfig::default(), frange, allowed);
165 assert_eq!(assists.len(), 1);
166
167 let mut assists = assists.iter();
168 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
169 }
170
171 {
172 let allowed = Some(vec![AssistKind::QuickFix]);
173 let assists = Assist::resolved(&db, &AssistConfig::default(), frange, allowed);
174 assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out");
175 }
176}