diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs index c0a0226fb2..cda2abfb9c 100644 --- a/crates/ra_assists/src/assist_config.rs +++ b/crates/ra_assists/src/assist_config.rs @@ -4,9 +4,12 @@ //! module, and we use to statically check that we only produce snippet //! assists if we are allowed to. +use crate::AssistKind; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct AssistConfig { pub snippet_cap: Option, + pub allowed: Option>, } impl AssistConfig { @@ -22,6 +25,6 @@ pub struct SnippetCap { impl Default for AssistConfig { fn default() -> Self { - AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } + AssistConfig { snippet_cap: Some(SnippetCap { _private: () }), allowed: None } } } diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index c335253635..3407df8562 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; use crate::{ assist_config::{AssistConfig, SnippetCap}, - Assist, AssistId, GroupLabel, ResolvedAssist, + Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, }; /// `AssistContext` allows to apply an assist or check if it could be applied. @@ -103,14 +103,26 @@ pub(crate) struct Assists { resolve: bool, file: FileId, buf: Vec<(Assist, Option)>, + allowed: Option>, } impl Assists { pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { - Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } + Assists { + resolve: true, + file: ctx.frange.file_id, + buf: Vec::new(), + allowed: ctx.config.allowed.clone(), + } } + pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { - Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } + Assists { + resolve: false, + file: ctx.frange.file_id, + buf: Vec::new(), + allowed: ctx.config.allowed.clone(), + } } pub(crate) fn finish_unresolved(self) -> Vec { @@ -139,9 +151,13 @@ impl Assists { target: TextRange, f: impl FnOnce(&mut AssistBuilder), ) -> Option<()> { + if !self.is_allowed(&id) { + return None; + } let label = Assist::new(id, label.into(), None, target); self.add_impl(label, f) } + pub(crate) fn add_group( &mut self, group: &GroupLabel, @@ -150,9 +166,14 @@ impl Assists { target: TextRange, f: impl FnOnce(&mut AssistBuilder), ) -> Option<()> { + if !self.is_allowed(&id) { + return None; + } + let label = Assist::new(id, label.into(), Some(group.clone()), target); self.add_impl(label, f) } + fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { let source_change = if self.resolve { let mut builder = AssistBuilder::new(self.file); @@ -170,6 +191,13 @@ impl Assists { self.buf.sort_by_key(|(label, _edit)| label.target.len()); self.buf } + + fn is_allowed(&self, id: &AssistId) -> bool { + match &self.allowed { + Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), + None => true, + } + } } pub(crate) struct AssistBuilder { diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3d61fbdedf..465b904151 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -37,6 +37,25 @@ pub enum AssistKind { RefactorRewrite, } +impl AssistKind { + pub fn contains(self, other: AssistKind) -> bool { + if self == other { + return true; + } + + match self { + AssistKind::None | AssistKind::Generate => return true, + AssistKind::Refactor => match other { + AssistKind::RefactorExtract + | AssistKind::RefactorInline + | AssistKind::RefactorRewrite => return true, + _ => return false, + }, + _ => return false, + } + } +} + /// Unique identifier of the assist, should not be shown to the user /// directly. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 858f5ca80f..18fcb90498 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -6,7 +6,7 @@ use ra_ide_db::RootDatabase; use ra_syntax::TextRange; use test_utils::{assert_eq_text, extract_offset, extract_range}; -use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; +use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; use stdx::trim_indent; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { @@ -134,3 +134,46 @@ fn assist_order_if_expr() { assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); } + +#[test] +fn assist_filter_works() { + let before = " + pub fn test_some_range(a: int) -> bool { + if let 2..6 = <|>5<|> { + true + } else { + false + } + }"; + let (range, before) = extract_range(before); + let (db, file_id) = with_single_file(&before); + let frange = FileRange { file_id, range }; + + { + let mut cfg = AssistConfig::default(); + cfg.allowed = Some(vec![AssistKind::Refactor]); + + let assists = Assist::resolved(&db, &cfg, frange); + let mut assists = assists.iter(); + + assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); + assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); + } + + { + let mut cfg = AssistConfig::default(); + cfg.allowed = Some(vec![AssistKind::RefactorExtract]); + let assists = Assist::resolved(&db, &cfg, frange); + assert_eq!(assists.len(), 1); + + let mut assists = assists.iter(); + assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); + } + + { + let mut cfg = AssistConfig::default(); + cfg.allowed = Some(vec![AssistKind::QuickFix]); + let assists = Assist::resolved(&db, &cfg, frange); + assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); + } +} diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 15b2811037..9f8ce3b991 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use ra_db::{FileId, FilePosition, FileRange}; -use ra_ide::{LineCol, LineIndex}; +use ra_ide::{AssistKind, LineCol, LineIndex}; use ra_syntax::{TextRange, TextSize}; use vfs::AbsPathBuf; @@ -52,3 +52,17 @@ pub(crate) fn file_range( let range = text_range(&line_index, range); Ok(FileRange { file_id, range }) } + +pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option { + let assist_kind = match &kind { + k if k == &lsp_types::CodeActionKind::EMPTY => AssistKind::None, + k if k == &lsp_types::CodeActionKind::QUICKFIX => AssistKind::QuickFix, + k if k == &lsp_types::CodeActionKind::REFACTOR => AssistKind::Refactor, + k if k == &lsp_types::CodeActionKind::REFACTOR_EXTRACT => AssistKind::RefactorExtract, + k if k == &lsp_types::CodeActionKind::REFACTOR_INLINE => AssistKind::RefactorInline, + k if k == &lsp_types::CodeActionKind::REFACTOR_REWRITE => AssistKind::RefactorRewrite, + _ => return None, + }; + + Some(assist_kind) +} diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index f2e24178ae..d28c700f14 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -746,6 +746,19 @@ fn handle_fixes( let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.analysis.file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.range); + + match ¶ms.context.only { + Some(v) => { + if !v.iter().any(|it| { + it == &lsp_types::CodeActionKind::EMPTY + || it == &lsp_types::CodeActionKind::QUICKFIX + }) { + return Ok(()); + } + } + None => {} + }; + let diagnostics = snap.analysis.diagnostics(file_id)?; let fixes_from_diagnostics = diagnostics @@ -777,7 +790,7 @@ fn handle_fixes( } pub(crate) fn handle_code_action( - snap: GlobalStateSnapshot, + mut snap: GlobalStateSnapshot, params: lsp_types::CodeActionParams, ) -> Result>> { let _p = profile("handle_code_action"); @@ -792,6 +805,13 @@ pub(crate) fn handle_code_action( let line_index = snap.analysis.file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.range); let frange = FileRange { file_id, range }; + + snap.config.assist.allowed = params + .clone() + .context + .only + .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); + let mut res: Vec = Vec::new(); handle_fixes(&snap, ¶ms, &mut res)?; @@ -812,7 +832,7 @@ pub(crate) fn handle_code_action( } pub(crate) fn handle_resolve_code_action( - snap: GlobalStateSnapshot, + mut snap: GlobalStateSnapshot, params: lsp_ext::ResolveCodeActionParams, ) -> Result> { let _p = profile("handle_resolve_code_action"); @@ -821,6 +841,12 @@ pub(crate) fn handle_resolve_code_action( let range = from_proto::text_range(&line_index, params.code_action_params.range); let frange = FileRange { file_id, range }; + snap.config.assist.allowed = params + .code_action_params + .context + .only + .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); + let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; let (id_string, index) = split_delim(¶ms.id, ':').unwrap(); let index = index.parse::().unwrap();