From 624eb1ee54e759c03d07c06e5e68dec7f36cb519 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 5 Jan 2021 16:57:05 +0300 Subject: More maintainable caps config The idea here is that we preserve client's config as is, without changes. This gets rid of state! --- crates/rust-analyzer/src/config.rs | 154 ++++++++++++--------- crates/rust-analyzer/src/handlers.rs | 19 ++- crates/rust-analyzer/src/lsp_utils.rs | 2 +- crates/rust-analyzer/src/reload.rs | 2 +- crates/rust-analyzer/src/to_proto.rs | 6 +- .../rust-analyzer/tests/rust-analyzer/support.rs | 24 +++- 6 files changed, 124 insertions(+), 83 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a5b1d90b1..a80652e83 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -175,7 +175,7 @@ config_data! { #[derive(Debug, Clone)] pub struct Config { - pub client_caps: ClientCapsConfig, + pub caps: lsp_types::ClientCapabilities, pub publish_diagnostics: bool, pub diagnostics: DiagnosticsConfig, @@ -286,26 +286,12 @@ pub struct RunnablesConfig { pub cargo_extra_args: Vec, } -#[derive(Debug, Clone, Default)] -pub struct ClientCapsConfig { - pub location_link: bool, - pub line_folding_only: bool, - pub hierarchical_symbols: bool, - pub code_action_literals: bool, - pub work_done_progress: bool, - pub code_action_group: bool, - pub code_action_resolve: bool, - pub hover_actions: bool, - pub status_notification: bool, - pub signature_help_label_offsets: bool, -} - impl Config { pub fn new(root_path: AbsPathBuf) -> Self { // Defaults here don't matter, we'll immediately re-write them with // ConfigData. let mut res = Config { - client_caps: ClientCapsConfig::default(), + caps: lsp_types::ClientCapabilities::default(), publish_diagnostics: false, diagnostics: DiagnosticsConfig::default(), @@ -505,38 +491,11 @@ impl Config { } pub fn update_caps(&mut self, caps: &ClientCapabilities) { + self.caps = caps.clone(); if let Some(doc_caps) = caps.text_document.as_ref() { if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { self.hover.markdown = value.contains(&MarkupKind::Markdown) } - if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { - self.client_caps.location_link = value; - } - if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only) - { - self.client_caps.line_folding_only = value - } - if let Some(value) = doc_caps - .document_symbol - .as_ref() - .and_then(|it| it.hierarchical_document_symbol_support) - { - self.client_caps.hierarchical_symbols = value - } - if let Some(value) = - doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some()) - { - self.client_caps.code_action_literals = value; - } - if let Some(value) = doc_caps - .signature_help - .as_ref() - .and_then(|it| it.signature_information.as_ref()) - .and_then(|it| it.parameter_information.as_ref()) - .and_then(|it| it.label_offset_support) - { - self.client_caps.signature_help_label_offsets = value; - } self.completion.allow_snippets(false); self.completion.active_resolve_capabilities = @@ -548,20 +507,6 @@ impl Config { } } } - - if let Some(code_action) = &doc_caps.code_action { - if let Some(resolve_support) = &code_action.resolve_support { - if resolve_support.properties.iter().any(|it| it == "edit") { - self.client_caps.code_action_resolve = true; - } - } - } - } - - if let Some(window_caps) = caps.window.as_ref() { - if let Some(value) = window_caps.work_done_progress { - self.client_caps.work_done_progress = value; - } } self.assist.allow_snippets(false); @@ -571,10 +516,6 @@ impl Config { let snippet_text_edit = get_bool("snippetTextEdit"); self.assist.allow_snippets(snippet_text_edit); - - self.client_caps.code_action_group = get_bool("codeActionGroup"); - self.client_caps.hover_actions = get_bool("hoverActions"); - self.client_caps.status_notification = get_bool("statusNotification"); } if let Some(workspace_caps) = caps.workspace.as_ref() { @@ -597,6 +538,95 @@ impl Config { } } +macro_rules! try_ { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} +macro_rules! try_or { + ($expr:expr, $or:expr) => { + try_!($expr).unwrap_or($or) + }; +} + +impl Config { + pub fn location_link(&self) -> bool { + try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) + } + pub fn line_folding_only(&self) -> bool { + try_or!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?, false) + } + pub fn hierarchical_symbols(&self) -> bool { + try_or!( + self.caps + .text_document + .as_ref()? + .document_symbol + .as_ref()? + .hierarchical_document_symbol_support?, + false + ) + } + pub fn code_action_literals(&self) -> bool { + try_!(self + .caps + .text_document + .as_ref()? + .code_action + .as_ref()? + .code_action_literal_support + .as_ref()?) + .is_some() + } + pub fn work_done_progress(&self) -> bool { + try_or!(self.caps.window.as_ref()?.work_done_progress?, false) + } + pub fn code_action_resolve(&self) -> bool { + try_or!( + self.caps + .text_document + .as_ref()? + .code_action + .as_ref()? + .resolve_support + .as_ref()? + .properties + .as_slice(), + &[] + ) + .iter() + .any(|it| it == "edit") + } + pub fn signature_help_label_offsets(&self) -> bool { + try_or!( + self.caps + .text_document + .as_ref()? + .signature_help + .as_ref()? + .signature_information + .as_ref()? + .parameter_information + .as_ref()? + .label_offset_support?, + false + ) + } + + fn experimental(&self, index: &'static str) -> bool { + try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false) + } + pub fn code_action_group(&self) -> bool { + self.experimental("codeActionGroup") + } + pub fn hover_actions(&self) -> bool { + self.experimental("hoverActions") + } + pub fn status_notification(&self) -> bool { + self.experimental("statusNotification") + } +} + #[derive(Deserialize)] #[serde(untagged)] enum ManifestOrProjectJson { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index c21ca044a..c13cdc4e3 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -320,7 +320,7 @@ pub(crate) fn handle_document_symbol( acc }; - let res = if snap.config.client_caps.hierarchical_symbols { + let res = if snap.config.hierarchical_symbols() { document_symbols.into() } else { let url = to_proto::url(&snap, file_id); @@ -727,7 +727,7 @@ pub(crate) fn handle_folding_range( let folds = snap.analysis.folding_ranges(file_id)?; let text = snap.analysis.file_text(file_id)?; let line_index = snap.analysis.file_line_index(file_id)?; - let line_folding_only = snap.config.client_caps.line_folding_only; + let line_folding_only = snap.config.line_folding_only(); let res = folds .into_iter() .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) @@ -746,11 +746,8 @@ pub(crate) fn handle_signature_help( None => return Ok(None), }; let concise = !snap.config.call_info_full; - let res = to_proto::signature_help( - call_info, - concise, - snap.config.client_caps.signature_help_label_offsets, - ); + let res = + to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets()); Ok(Some(res)) } @@ -929,7 +926,7 @@ pub(crate) fn handle_code_action( // We intentionally don't support command-based actions, as those either // requires custom client-code anyway, or requires server-initiated edits. // Server initiated edits break causality, so we avoid those as well. - if !snap.config.client_caps.code_action_literals { + if !snap.config.code_action_literals() { return Ok(None); } @@ -959,7 +956,7 @@ pub(crate) fn handle_code_action( add_quick_fixes(&snap, frange, &line_index, &mut res)?; } - if snap.config.client_caps.code_action_resolve { + if snap.config.code_action_resolve() { for (index, assist) in snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() { @@ -1542,7 +1539,7 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command { } fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option { - let value = if snap.config.client_caps.location_link { + let value = if snap.config.location_link() { let link = to_proto::location_link(snap, None, nav.clone()).ok()?; to_value(link).ok()? } else { @@ -1641,7 +1638,7 @@ fn prepare_hover_actions( file_id: FileId, actions: &[HoverAction], ) -> Vec { - if snap.config.hover.none() || !snap.config.client_caps.hover_actions { + if snap.config.hover.none() || !snap.config.hover_actions() { return Vec::new(); } diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 60c12e4e2..40de56dad 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -46,7 +46,7 @@ impl GlobalState { message: Option, fraction: Option, ) { - if !self.config.client_caps.work_done_progress { + if !self.config.work_done_progress() { return; } let percentage = fraction.map(|f| { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 79e39e3a5..ce5cedeb3 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -79,7 +79,7 @@ impl GlobalState { } pub(crate) fn transition(&mut self, new_status: Status) { self.status = new_status; - if self.config.client_caps.status_notification { + if self.config.status_notification() { let lsp_status = match new_status { Status::Loading => lsp_ext::Status::Loading, Status::Ready => lsp_ext::Status::Ready, diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 999b18351..e0413ec06 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -605,7 +605,7 @@ pub(crate) fn goto_definition_response( src: Option, targets: Vec, ) -> Result { - if snap.config.client_caps.location_link { + if snap.config.location_link() { let links = targets .into_iter() .map(|nav| location_link(snap, src, nav)) @@ -785,7 +785,7 @@ pub(crate) fn unresolved_code_action( assert!(assist.source_change.is_none()); let res = lsp_ext::CodeAction { title: assist.label.to_string(), - group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), + group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), kind: Some(code_action_kind(assist.id.1)), edit: None, is_preferred: None, @@ -805,7 +805,7 @@ pub(crate) fn resolved_code_action( let res = lsp_ext::CodeAction { edit: Some(snippet_workspace_edit(snap, change)?), title: assist.label.to_string(), - group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), + group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), kind: Some(code_action_kind(assist.id.1)), is_preferred: None, data: None, diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 456125789..aac7dbcce 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs @@ -14,7 +14,7 @@ use lsp_types::{ use lsp_types::{ProgressParams, ProgressParamsValue}; use project_model::{CargoConfig, ProjectManifest}; use rust_analyzer::{ - config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, + config::{Config, FilesConfig, FilesWatcher, LinkedProject}, main_loop, }; use serde::Serialize; @@ -84,10 +84,24 @@ impl<'a> Project<'a> { .collect::>(); let mut config = Config { - client_caps: ClientCapsConfig { - location_link: true, - code_action_literals: true, - work_done_progress: true, + caps: lsp_types::ClientCapabilities { + text_document: Some(lsp_types::TextDocumentClientCapabilities { + definition: Some(lsp_types::GotoCapability { + link_support: Some(true), + ..Default::default() + }), + code_action: Some(lsp_types::CodeActionClientCapabilities { + code_action_literal_support: Some( + lsp_types::CodeActionLiteralSupport::default(), + ), + ..Default::default() + }), + ..Default::default() + }), + window: Some(lsp_types::WindowClientCapabilities { + work_done_progress: Some(true), + ..Default::default() + }), ..Default::default() }, cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, -- cgit v1.2.3