9732: feat: gate custom clint-side commands behind capabilities r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2021-07-30 16:27:10 +00:00 committed by GitHub
commit 33f12a3608
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 124 deletions

View file

@ -25,9 +25,12 @@ use serde::{de::DeserializeOwned, Deserialize};
use vfs::AbsPathBuf;
use crate::{
caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig,
line_index::OffsetEncoding, lsp_ext::supports_utf8, lsp_ext::WorkspaceSymbolSearchKind,
caps::completion_item_edit_resolve,
diagnostics::DiagnosticsMapConfig,
line_index::OffsetEncoding,
lsp_ext::supports_utf8,
lsp_ext::WorkspaceSymbolSearchScope,
lsp_ext::{self, WorkspaceSymbolSearchKind},
};
// Defines the server-side configuration of the rust-analyzer. We generate
@ -221,6 +224,9 @@ config_data! {
/// Whether to show `References` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_references: bool = "false",
/// Internal config: use custom client-side commands even when the
/// client doesn't set the corresponding capability.
lens_forceCustomCommands: bool = "true",
/// Disable project auto-discovery in favor of explicitly specified set
/// of projects.
@ -405,6 +411,14 @@ pub struct WorkspaceSymbolConfig {
pub search_kind: WorkspaceSymbolSearchKind,
}
pub struct ClientCommandsConfig {
pub run_single: bool,
pub debug_single: bool,
pub show_reference: bool,
pub goto_location: bool,
pub trigger_parameter_hints: bool,
}
impl Config {
pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
Config {
@ -858,6 +872,24 @@ impl Config {
false
)
}
pub fn client_commands(&self) -> ClientCommandsConfig {
let commands =
try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
let commands: Option<lsp_ext::ClientCommandOptions> =
serde_json::from_value(commands.clone()).ok();
let force = commands.is_none() && self.data.lens_forceCustomCommands;
let commands = commands.map(|it| it.commands).unwrap_or_default();
let get = |name: &str| commands.iter().any(|it| it == name) || force;
ClientCommandsConfig {
run_single: get("rust-analyzer.runSingle"),
debug_single: get("rust-analyzer.debugSingle"),
show_reference: get("rust-analyzer.showReferences"),
goto_location: get("rust-analyzer.gotoLocation"),
trigger_parameter_hints: get("editor.action.triggerParameterHints"),
}
}
pub fn highlight_related(&self) -> HighlightRelatedConfig {
HighlightRelatedConfig {

View file

@ -768,13 +768,8 @@ pub(crate) fn handle_completion(
};
let line_index = snap.file_line_index(position.file_id)?;
let items = to_proto::completion_items(
snap.config.insert_replace_support(),
completion_config.enable_imports_on_the_fly,
&line_index,
text_document_position,
items,
);
let items =
to_proto::completion_items(&snap.config, &line_index, text_document_position, items);
let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
Ok(Some(completion_list.into()))
@ -1503,7 +1498,7 @@ fn show_impl_command_link(
snap: &GlobalStateSnapshot,
position: &FilePosition,
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover_actions().implementations {
if snap.config.hover_actions().implementations && snap.config.client_commands().show_reference {
if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
@ -1529,7 +1524,7 @@ fn show_ref_command_link(
snap: &GlobalStateSnapshot,
position: &FilePosition,
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover_actions().references {
if snap.config.hover_actions().references && snap.config.client_commands().show_reference {
if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
@ -1559,35 +1554,47 @@ fn runnable_action_links(
snap: &GlobalStateSnapshot,
runnable: Runnable,
) -> Option<lsp_ext::CommandLinkGroup> {
let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
let hover_actions_config = snap.config.hover_actions();
if !hover_actions_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
if !hover_actions_config.runnable() {
return None;
}
let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
if should_skip_target(&runnable, cargo_spec.as_ref()) {
return None;
}
let client_commands_config = snap.config.client_commands();
if !(client_commands_config.run_single || client_commands_config.debug_single) {
return None;
}
let title = runnable.title();
to_proto::runnable(snap, runnable).ok().map(|r| {
let mut group = lsp_ext::CommandLinkGroup::default();
let r = to_proto::runnable(snap, runnable).ok()?;
if hover_actions_config.run {
let run_command = to_proto::command::run_single(&r, &title);
group.commands.push(to_command_link(run_command, r.label.clone()));
}
let mut group = lsp_ext::CommandLinkGroup::default();
if hover_actions_config.debug {
let dbg_command = to_proto::command::debug_single(&r);
group.commands.push(to_command_link(dbg_command, r.label));
}
if hover_actions_config.run && client_commands_config.run_single {
let run_command = to_proto::command::run_single(&r, &title);
group.commands.push(to_command_link(run_command, r.label.clone()));
}
group
})
if hover_actions_config.debug && client_commands_config.debug_single {
let dbg_command = to_proto::command::debug_single(&r);
group.commands.push(to_command_link(dbg_command, r.label));
}
Some(group)
}
fn goto_type_action_links(
snap: &GlobalStateSnapshot,
nav_targets: &[HoverGotoTypeData],
) -> Option<lsp_ext::CommandLinkGroup> {
if !snap.config.hover_actions().goto_type_def || nav_targets.is_empty() {
if !snap.config.hover_actions().goto_type_def
|| nav_targets.is_empty()
|| !snap.config.client_commands().goto_location
{
return None;
}

View file

@ -523,3 +523,8 @@ pub struct CompletionResolveData {
pub full_import_path: String,
pub imported_name: String,
}
#[derive(Debug, Deserialize, Default)]
pub struct ClientCommandOptions {
pub commands: Vec<String>,
}

View file

@ -18,6 +18,7 @@ use vfs::AbsPath;
use crate::{
cargo_target_spec::CargoTargetSpec,
config::Config,
global_state::GlobalStateSnapshot,
line_index::{LineEndings, LineIndex, OffsetEncoding},
lsp_ext, semantic_tokens, Result,
@ -190,8 +191,7 @@ pub(crate) fn snippet_text_edit_vec(
}
pub(crate) fn completion_items(
insert_replace_support: bool,
enable_imports_on_the_fly: bool,
config: &Config,
line_index: &LineIndex,
tdpp: lsp_types::TextDocumentPositionParams,
items: Vec<CompletionItem>,
@ -199,23 +199,14 @@ pub(crate) fn completion_items(
let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
let mut res = Vec::with_capacity(items.len());
for item in items {
completion_item(
&mut res,
insert_replace_support,
enable_imports_on_the_fly,
line_index,
&tdpp,
max_relevance,
item,
)
completion_item(&mut res, config, line_index, &tdpp, max_relevance, item)
}
res
}
fn completion_item(
acc: &mut Vec<lsp_types::CompletionItem>,
insert_replace_support: bool,
enable_imports_on_the_fly: bool,
config: &Config,
line_index: &LineIndex,
tdpp: &lsp_types::TextDocumentPositionParams,
max_relevance: u32,
@ -230,7 +221,7 @@ fn completion_item(
let source_range = item.source_range();
for indel in item.text_edit().iter() {
if indel.delete.contains_range(source_range) {
let insert_replace_support = insert_replace_support.then(|| tdpp.position);
let insert_replace_support = config.insert_replace_support().then(|| tdpp.position);
text_edit = Some(if indel.delete == source_range {
self::completion_text_edit(line_index, insert_replace_support, indel.clone())
} else {
@ -269,14 +260,14 @@ fn completion_item(
lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
}
if item.trigger_call_info() {
if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
lsp_item.command = Some(command::trigger_parameter_hints());
}
if item.is_snippet() {
lsp_item.insert_text_format = Some(lsp_types::InsertTextFormat::Snippet);
}
if enable_imports_on_the_fly {
if config.completion().enable_imports_on_the_fly {
if let Some(import_edit) = item.import_to_add() {
let import_path = &import_edit.import.import_path;
if let Some(import_name) = import_path.segments().last() {
@ -992,6 +983,7 @@ pub(crate) fn code_lens(
snap: &GlobalStateSnapshot,
annotation: Annotation,
) -> Result<()> {
let client_commands_config = snap.config.client_commands();
match annotation.kind {
AnnotationKind::Runnable(run) => {
let line_index = snap.file_line_index(run.nav.file_id)?;
@ -1008,7 +1000,7 @@ pub(crate) fn code_lens(
let r = runnable(snap, run)?;
let lens_config = snap.config.lens();
if lens_config.run {
if lens_config.run && client_commands_config.run_single {
let command = command::run_single(&r, &title);
acc.push(lsp_types::CodeLens {
range: annotation_range,
@ -1016,7 +1008,7 @@ pub(crate) fn code_lens(
data: None,
})
}
if lens_config.debug && can_debug {
if lens_config.debug && can_debug && client_commands_config.debug_single {
let command = command::debug_single(&r);
acc.push(lsp_types::CodeLens {
range: annotation_range,
@ -1026,6 +1018,9 @@ pub(crate) fn code_lens(
}
}
AnnotationKind::HasImpls { position: file_position, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
let line_index = snap.file_line_index(file_position.file_id)?;
let annotation_range = range(&line_index, annotation.range);
let url = url(snap, file_position.file_id);
@ -1069,6 +1064,9 @@ pub(crate) fn code_lens(
})
}
AnnotationKind::HasReferences { position: file_position, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
let line_index = snap.file_line_index(file_position.file_id)?;
let annotation_range = range(&line_index, annotation.range);
let url = url(snap, file_position.file_id);
@ -1207,88 +1205,9 @@ mod tests {
use std::sync::Arc;
use ide::Analysis;
use ide_db::helpers::{
insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
SnippetCap,
};
use super::*;
#[test]
fn test_completion_with_ref() {
let fixture = r#"
struct Foo;
fn foo(arg: &Foo) {}
fn main() {
let arg = Foo;
foo($0)
}"#;
let (offset, text) = test_utils::extract_offset(fixture);
let line_index = LineIndex {
index: Arc::new(ide::LineIndex::new(&text)),
endings: LineEndings::Unix,
encoding: OffsetEncoding::Utf16,
};
let (analysis, file_id) = Analysis::from_single_file(text);
let file_position = ide_db::base_db::FilePosition { file_id, offset };
let mut items = analysis
.completions(
&ide::CompletionConfig {
enable_postfix_completions: true,
enable_imports_on_the_fly: true,
enable_self_on_the_fly: true,
add_call_parenthesis: true,
add_call_argument_snippets: true,
snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig {
granularity: ImportGranularity::Item,
prefix_kind: PrefixKind::Plain,
enforce_granularity: true,
group: true,
skip_glob_imports: true,
},
},
file_position,
)
.unwrap()
.unwrap();
items.retain(|c| c.label().ends_with("arg"));
let items = completion_items(
false,
false,
&line_index,
lsp_types::TextDocumentPositionParams {
text_document: lsp_types::TextDocumentIdentifier {
uri: "file://main.rs".parse().unwrap(),
},
position: position(&line_index, file_position.offset),
},
items,
);
let items: Vec<(String, Option<String>)> =
items.into_iter().map(|c| (c.label, c.sort_text)).collect();
expect_test::expect![[r#"
[
(
"&arg",
Some(
"fffffff9",
),
),
(
"arg",
Some(
"fffffffd",
),
),
]
"#]]
.assert_debug_eq(&items);
}
#[test]
fn conv_fold_line_folding_only_fixup() {
let text = r#"mod a;