aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_config.rs5
-rw-r--r--crates/ra_assists/src/assist_context.rs34
-rw-r--r--crates/ra_assists/src/lib.rs19
-rw-r--r--crates/ra_assists/src/tests.rs45
-rw-r--r--crates/rust-analyzer/src/from_proto.rs16
-rw-r--r--crates/rust-analyzer/src/handlers.rs30
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
7use crate::AssistKind;
8
7#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AssistConfig { 10pub struct AssistConfig {
9 pub snippet_cap: Option<SnippetCap>, 11 pub snippet_cap: Option<SnippetCap>,
12 pub allowed: Option<Vec<AssistKind>>,
10} 13}
11 14
12impl AssistConfig { 15impl AssistConfig {
@@ -22,6 +25,6 @@ pub struct SnippetCap {
22 25
23impl Default for AssistConfig { 26impl 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
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.
@@ -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
108impl Assists { 109impl 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
175pub(crate) struct AssistBuilder { 203pub(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
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)]
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;
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) {
@@ -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]
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 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 @@
2use std::convert::TryFrom; 2use std::convert::TryFrom;
3 3
4use ra_db::{FileId, FilePosition, FileRange}; 4use ra_db::{FileId, FilePosition, FileRange};
5use ra_ide::{LineCol, LineIndex}; 5use ra_ide::{AssistKind, LineCol, LineIndex};
6use ra_syntax::{TextRange, TextSize}; 6use ra_syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf; 7use 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
56pub(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, &params.text_document.uri)?; 746 let file_id = from_proto::file_id(&snap, &params.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 &params.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
779pub(crate) fn handle_code_action( 792pub(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, &params, &mut res)?; 817 handle_fixes(&snap, &params, &mut res)?;
@@ -812,7 +832,7 @@ pub(crate) fn handle_code_action(
812} 832}
813 833
814pub(crate) fn handle_resolve_code_action( 834pub(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(&params.id, ':').unwrap(); 851 let (id_string, index) = split_delim(&params.id, ':').unwrap();
826 let index = index.parse::<usize>().unwrap(); 852 let index = index.parse::<usize>().unwrap();