diff options
-rw-r--r-- | crates/ra_assists/src/assist_config.rs | 5 | ||||
-rw-r--r-- | crates/ra_assists/src/assist_context.rs | 34 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 19 | ||||
-rw-r--r-- | crates/ra_assists/src/tests.rs | 45 | ||||
-rw-r--r-- | crates/rust-analyzer/src/from_proto.rs | 16 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 30 |
6 files changed, 141 insertions, 8 deletions
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs index c0a0226fb..cda2abfb9 100644 --- a/crates/ra_assists/src/assist_config.rs +++ b/crates/ra_assists/src/assist_config.rs | |||
@@ -4,9 +4,12 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! assists if we are allowed to. | 5 | //! assists if we are allowed to. |
6 | 6 | ||
7 | use crate::AssistKind; | ||
8 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
8 | pub struct AssistConfig { | 10 | pub struct AssistConfig { |
9 | pub snippet_cap: Option<SnippetCap>, | 11 | pub snippet_cap: Option<SnippetCap>, |
12 | pub allowed: Option<Vec<AssistKind>>, | ||
10 | } | 13 | } |
11 | 14 | ||
12 | impl AssistConfig { | 15 | impl AssistConfig { |
@@ -22,6 +25,6 @@ pub struct SnippetCap { | |||
22 | 25 | ||
23 | impl Default for AssistConfig { | 26 | impl Default for AssistConfig { |
24 | fn default() -> Self { | 27 | fn default() -> Self { |
25 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } | 28 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }), allowed: None } |
26 | } | 29 | } |
27 | } | 30 | } |
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index c33525363..3407df856 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 | ||
20 | use crate::{ | 20 | use 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. |
@@ -103,14 +103,26 @@ pub(crate) struct Assists { | |||
103 | resolve: bool, | 103 | resolve: bool, |
104 | file: FileId, | 104 | file: FileId, |
105 | buf: Vec<(Assist, Option<SourceChange>)>, | 105 | buf: Vec<(Assist, Option<SourceChange>)>, |
106 | allowed: Option<Vec<AssistKind>>, | ||
106 | } | 107 | } |
107 | 108 | ||
108 | impl Assists { | 109 | impl Assists { |
109 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 110 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { |
110 | 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 | } | ||
111 | } | 117 | } |
118 | |||
112 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 119 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { |
113 | 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 | } | ||
114 | } | 126 | } |
115 | 127 | ||
116 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | 128 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { |
@@ -139,9 +151,13 @@ impl Assists { | |||
139 | target: TextRange, | 151 | target: TextRange, |
140 | f: impl FnOnce(&mut AssistBuilder), | 152 | f: impl FnOnce(&mut AssistBuilder), |
141 | ) -> Option<()> { | 153 | ) -> Option<()> { |
154 | if !self.is_allowed(&id) { | ||
155 | return None; | ||
156 | } | ||
142 | let label = Assist::new(id, label.into(), None, target); | 157 | let label = Assist::new(id, label.into(), None, target); |
143 | self.add_impl(label, f) | 158 | self.add_impl(label, f) |
144 | } | 159 | } |
160 | |||
145 | pub(crate) fn add_group( | 161 | pub(crate) fn add_group( |
146 | &mut self, | 162 | &mut self, |
147 | group: &GroupLabel, | 163 | group: &GroupLabel, |
@@ -150,9 +166,14 @@ impl Assists { | |||
150 | target: TextRange, | 166 | target: TextRange, |
151 | f: impl FnOnce(&mut AssistBuilder), | 167 | f: impl FnOnce(&mut AssistBuilder), |
152 | ) -> Option<()> { | 168 | ) -> Option<()> { |
169 | if !self.is_allowed(&id) { | ||
170 | return None; | ||
171 | } | ||
172 | |||
153 | let label = Assist::new(id, label.into(), Some(group.clone()), target); | 173 | let label = Assist::new(id, label.into(), Some(group.clone()), target); |
154 | self.add_impl(label, f) | 174 | self.add_impl(label, f) |
155 | } | 175 | } |
176 | |||
156 | 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<()> { |
157 | let source_change = if self.resolve { | 178 | let source_change = if self.resolve { |
158 | let mut builder = AssistBuilder::new(self.file); | 179 | let mut builder = AssistBuilder::new(self.file); |
@@ -170,6 +191,13 @@ impl Assists { | |||
170 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | 191 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); |
171 | self.buf | 192 | self.buf |
172 | } | 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 | } | ||
173 | } | 201 | } |
174 | 202 | ||
175 | pub(crate) struct AssistBuilder { | 203 | pub(crate) struct AssistBuilder { |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3d61fbded..465b90415 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 | ||
40 | impl 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)] |
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 858f5ca80..18fcb9049 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs | |||
@@ -6,7 +6,7 @@ use ra_ide_db::RootDatabase; | |||
6 | use ra_syntax::TextRange; | 6 | use ra_syntax::TextRange; |
7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; | 7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; |
8 | 8 | ||
9 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; | 9 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; |
10 | use stdx::trim_indent; | 10 | use stdx::trim_indent; |
11 | 11 | ||
12 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 12 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
@@ -134,3 +134,46 @@ fn assist_order_if_expr() { | |||
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] | ||
139 | fn 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 mut cfg = AssistConfig::default(); | ||
154 | cfg.allowed = Some(vec![AssistKind::Refactor]); | ||
155 | |||
156 | let assists = Assist::resolved(&db, &cfg, frange); | ||
157 | let mut assists = assists.iter(); | ||
158 | |||
159 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | ||
160 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | ||
161 | } | ||
162 | |||
163 | { | ||
164 | let mut cfg = AssistConfig::default(); | ||
165 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | ||
166 | let assists = Assist::resolved(&db, &cfg, frange); | ||
167 | assert_eq!(assists.len(), 1); | ||
168 | |||
169 | let mut assists = assists.iter(); | ||
170 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | ||
171 | } | ||
172 | |||
173 | { | ||
174 | let mut cfg = AssistConfig::default(); | ||
175 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | ||
176 | let assists = Assist::resolved(&db, &cfg, frange); | ||
177 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); | ||
178 | } | ||
179 | } | ||
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 15b281103..9f8ce3b99 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | use std::convert::TryFrom; | 2 | use std::convert::TryFrom; |
3 | 3 | ||
4 | use ra_db::{FileId, FilePosition, FileRange}; | 4 | use ra_db::{FileId, FilePosition, FileRange}; |
5 | use ra_ide::{LineCol, LineIndex}; | 5 | use ra_ide::{AssistKind, LineCol, LineIndex}; |
6 | use ra_syntax::{TextRange, TextSize}; | 6 | use ra_syntax::{TextRange, TextSize}; |
7 | use vfs::AbsPathBuf; | 7 | use vfs::AbsPathBuf; |
8 | 8 | ||
@@ -52,3 +52,17 @@ pub(crate) fn file_range( | |||
52 | let range = text_range(&line_index, range); | 52 | let range = text_range(&line_index, range); |
53 | Ok(FileRange { file_id, range }) | 53 | Ok(FileRange { file_id, range }) |
54 | } | 54 | } |
55 | |||
56 | pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> { | ||
57 | let assist_kind = match &kind { | ||
58 | k if k == &lsp_types::CodeActionKind::EMPTY => AssistKind::None, | ||
59 | k if k == &lsp_types::CodeActionKind::QUICKFIX => AssistKind::QuickFix, | ||
60 | k if k == &lsp_types::CodeActionKind::REFACTOR => AssistKind::Refactor, | ||
61 | k if k == &lsp_types::CodeActionKind::REFACTOR_EXTRACT => AssistKind::RefactorExtract, | ||
62 | k if k == &lsp_types::CodeActionKind::REFACTOR_INLINE => AssistKind::RefactorInline, | ||
63 | k if k == &lsp_types::CodeActionKind::REFACTOR_REWRITE => AssistKind::RefactorRewrite, | ||
64 | _ => return None, | ||
65 | }; | ||
66 | |||
67 | Some(assist_kind) | ||
68 | } | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index f2e24178a..d28c700f1 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -746,6 +746,19 @@ fn handle_fixes( | |||
746 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 746 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
747 | let line_index = snap.analysis.file_line_index(file_id)?; | 747 | let line_index = snap.analysis.file_line_index(file_id)?; |
748 | let range = from_proto::text_range(&line_index, params.range); | 748 | let range = from_proto::text_range(&line_index, params.range); |
749 | |||
750 | match ¶ms.context.only { | ||
751 | Some(v) => { | ||
752 | if !v.iter().any(|it| { | ||
753 | it == &lsp_types::CodeActionKind::EMPTY | ||
754 | || it == &lsp_types::CodeActionKind::QUICKFIX | ||
755 | }) { | ||
756 | return Ok(()); | ||
757 | } | ||
758 | } | ||
759 | None => {} | ||
760 | }; | ||
761 | |||
749 | let diagnostics = snap.analysis.diagnostics(file_id)?; | 762 | let diagnostics = snap.analysis.diagnostics(file_id)?; |
750 | 763 | ||
751 | let fixes_from_diagnostics = diagnostics | 764 | let fixes_from_diagnostics = diagnostics |
@@ -777,7 +790,7 @@ fn handle_fixes( | |||
777 | } | 790 | } |
778 | 791 | ||
779 | pub(crate) fn handle_code_action( | 792 | pub(crate) fn handle_code_action( |
780 | snap: GlobalStateSnapshot, | 793 | mut snap: GlobalStateSnapshot, |
781 | params: lsp_types::CodeActionParams, | 794 | params: lsp_types::CodeActionParams, |
782 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 795 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
783 | let _p = profile("handle_code_action"); | 796 | let _p = profile("handle_code_action"); |
@@ -792,6 +805,13 @@ pub(crate) fn handle_code_action( | |||
792 | let line_index = snap.analysis.file_line_index(file_id)?; | 805 | let line_index = snap.analysis.file_line_index(file_id)?; |
793 | let range = from_proto::text_range(&line_index, params.range); | 806 | let range = from_proto::text_range(&line_index, params.range); |
794 | let frange = FileRange { file_id, range }; | 807 | let frange = FileRange { file_id, range }; |
808 | |||
809 | snap.config.assist.allowed = params | ||
810 | .clone() | ||
811 | .context | ||
812 | .only | ||
813 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | ||
814 | |||
795 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 815 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
796 | 816 | ||
797 | handle_fixes(&snap, ¶ms, &mut res)?; | 817 | handle_fixes(&snap, ¶ms, &mut res)?; |
@@ -812,7 +832,7 @@ pub(crate) fn handle_code_action( | |||
812 | } | 832 | } |
813 | 833 | ||
814 | pub(crate) fn handle_resolve_code_action( | 834 | pub(crate) fn handle_resolve_code_action( |
815 | snap: GlobalStateSnapshot, | 835 | mut snap: GlobalStateSnapshot, |
816 | params: lsp_ext::ResolveCodeActionParams, | 836 | params: lsp_ext::ResolveCodeActionParams, |
817 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { | 837 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { |
818 | let _p = profile("handle_resolve_code_action"); | 838 | let _p = profile("handle_resolve_code_action"); |
@@ -821,6 +841,12 @@ pub(crate) fn handle_resolve_code_action( | |||
821 | let range = from_proto::text_range(&line_index, params.code_action_params.range); | 841 | let range = from_proto::text_range(&line_index, params.code_action_params.range); |
822 | let frange = FileRange { file_id, range }; | 842 | let frange = FileRange { file_id, range }; |
823 | 843 | ||
844 | snap.config.assist.allowed = params | ||
845 | .code_action_params | ||
846 | .context | ||
847 | .only | ||
848 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | ||
849 | |||
824 | let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; | 850 | let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; |
825 | let (id_string, index) = split_delim(¶ms.id, ':').unwrap(); | 851 | let (id_string, index) = split_delim(¶ms.id, ':').unwrap(); |
826 | let index = index.parse::<usize>().unwrap(); | 852 | let index = index.parse::<usize>().unwrap(); |