aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-01-05 13:57:05 +0000
committerAleksey Kladov <[email protected]>2021-01-05 14:46:57 +0000
commit624eb1ee54e759c03d07c06e5e68dec7f36cb519 (patch)
treee7ee04b6a3cb030e4db0c3d118d723b8cf3704e0
parentc8d3d5694be065ffb1d52bc33e098ff610693097 (diff)
More maintainable caps config
The idea here is that we preserve client's config as is, without changes. This gets rid of state!
-rw-r--r--crates/rust-analyzer/src/config.rs154
-rw-r--r--crates/rust-analyzer/src/handlers.rs19
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs6
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs24
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! {
175 175
176#[derive(Debug, Clone)] 176#[derive(Debug, Clone)]
177pub struct Config { 177pub struct Config {
178 pub client_caps: ClientCapsConfig, 178 pub caps: lsp_types::ClientCapabilities,
179 179
180 pub publish_diagnostics: bool, 180 pub publish_diagnostics: bool,
181 pub diagnostics: DiagnosticsConfig, 181 pub diagnostics: DiagnosticsConfig,
@@ -286,26 +286,12 @@ pub struct RunnablesConfig {
286 pub cargo_extra_args: Vec<String>, 286 pub cargo_extra_args: Vec<String>,
287} 287}
288 288
289#[derive(Debug, Clone, Default)]
290pub struct ClientCapsConfig {
291 pub location_link: bool,
292 pub line_folding_only: bool,
293 pub hierarchical_symbols: bool,
294 pub code_action_literals: bool,
295 pub work_done_progress: bool,
296 pub code_action_group: bool,
297 pub code_action_resolve: bool,
298 pub hover_actions: bool,
299 pub status_notification: bool,
300 pub signature_help_label_offsets: bool,
301}
302
303impl Config { 289impl Config {
304 pub fn new(root_path: AbsPathBuf) -> Self { 290 pub fn new(root_path: AbsPathBuf) -> Self {
305 // Defaults here don't matter, we'll immediately re-write them with 291 // Defaults here don't matter, we'll immediately re-write them with
306 // ConfigData. 292 // ConfigData.
307 let mut res = Config { 293 let mut res = Config {
308 client_caps: ClientCapsConfig::default(), 294 caps: lsp_types::ClientCapabilities::default(),
309 295
310 publish_diagnostics: false, 296 publish_diagnostics: false,
311 diagnostics: DiagnosticsConfig::default(), 297 diagnostics: DiagnosticsConfig::default(),
@@ -505,38 +491,11 @@ impl Config {
505 } 491 }
506 492
507 pub fn update_caps(&mut self, caps: &ClientCapabilities) { 493 pub fn update_caps(&mut self, caps: &ClientCapabilities) {
494 self.caps = caps.clone();
508 if let Some(doc_caps) = caps.text_document.as_ref() { 495 if let Some(doc_caps) = caps.text_document.as_ref() {
509 if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { 496 if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) {
510 self.hover.markdown = value.contains(&MarkupKind::Markdown) 497 self.hover.markdown = value.contains(&MarkupKind::Markdown)
511 } 498 }
512 if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
513 self.client_caps.location_link = value;
514 }
515 if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
516 {
517 self.client_caps.line_folding_only = value
518 }
519 if let Some(value) = doc_caps
520 .document_symbol
521 .as_ref()
522 .and_then(|it| it.hierarchical_document_symbol_support)
523 {
524 self.client_caps.hierarchical_symbols = value
525 }
526 if let Some(value) =
527 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
528 {
529 self.client_caps.code_action_literals = value;
530 }
531 if let Some(value) = doc_caps
532 .signature_help
533 .as_ref()
534 .and_then(|it| it.signature_information.as_ref())
535 .and_then(|it| it.parameter_information.as_ref())
536 .and_then(|it| it.label_offset_support)
537 {
538 self.client_caps.signature_help_label_offsets = value;
539 }
540 499
541 self.completion.allow_snippets(false); 500 self.completion.allow_snippets(false);
542 self.completion.active_resolve_capabilities = 501 self.completion.active_resolve_capabilities =
@@ -548,20 +507,6 @@ impl Config {
548 } 507 }
549 } 508 }
550 } 509 }
551
552 if let Some(code_action) = &doc_caps.code_action {
553 if let Some(resolve_support) = &code_action.resolve_support {
554 if resolve_support.properties.iter().any(|it| it == "edit") {
555 self.client_caps.code_action_resolve = true;
556 }
557 }
558 }
559 }
560
561 if let Some(window_caps) = caps.window.as_ref() {
562 if let Some(value) = window_caps.work_done_progress {
563 self.client_caps.work_done_progress = value;
564 }
565 } 510 }
566 511
567 self.assist.allow_snippets(false); 512 self.assist.allow_snippets(false);
@@ -571,10 +516,6 @@ impl Config {
571 516
572 let snippet_text_edit = get_bool("snippetTextEdit"); 517 let snippet_text_edit = get_bool("snippetTextEdit");
573 self.assist.allow_snippets(snippet_text_edit); 518 self.assist.allow_snippets(snippet_text_edit);
574
575 self.client_caps.code_action_group = get_bool("codeActionGroup");
576 self.client_caps.hover_actions = get_bool("hoverActions");
577 self.client_caps.status_notification = get_bool("statusNotification");
578 } 519 }
579 520
580 if let Some(workspace_caps) = caps.workspace.as_ref() { 521 if let Some(workspace_caps) = caps.workspace.as_ref() {
@@ -597,6 +538,95 @@ impl Config {
597 } 538 }
598} 539}
599 540
541macro_rules! try_ {
542 ($expr:expr) => {
543 || -> _ { Some($expr) }()
544 };
545}
546macro_rules! try_or {
547 ($expr:expr, $or:expr) => {
548 try_!($expr).unwrap_or($or)
549 };
550}
551
552impl Config {
553 pub fn location_link(&self) -> bool {
554 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false)
555 }
556 pub fn line_folding_only(&self) -> bool {
557 try_or!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?, false)
558 }
559 pub fn hierarchical_symbols(&self) -> bool {
560 try_or!(
561 self.caps
562 .text_document
563 .as_ref()?
564 .document_symbol
565 .as_ref()?
566 .hierarchical_document_symbol_support?,
567 false
568 )
569 }
570 pub fn code_action_literals(&self) -> bool {
571 try_!(self
572 .caps
573 .text_document
574 .as_ref()?
575 .code_action
576 .as_ref()?
577 .code_action_literal_support
578 .as_ref()?)
579 .is_some()
580 }
581 pub fn work_done_progress(&self) -> bool {
582 try_or!(self.caps.window.as_ref()?.work_done_progress?, false)
583 }
584 pub fn code_action_resolve(&self) -> bool {
585 try_or!(
586 self.caps
587 .text_document
588 .as_ref()?
589 .code_action
590 .as_ref()?
591 .resolve_support
592 .as_ref()?
593 .properties
594 .as_slice(),
595 &[]
596 )
597 .iter()
598 .any(|it| it == "edit")
599 }
600 pub fn signature_help_label_offsets(&self) -> bool {
601 try_or!(
602 self.caps
603 .text_document
604 .as_ref()?
605 .signature_help
606 .as_ref()?
607 .signature_information
608 .as_ref()?
609 .parameter_information
610 .as_ref()?
611 .label_offset_support?,
612 false
613 )
614 }
615
616 fn experimental(&self, index: &'static str) -> bool {
617 try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false)
618 }
619 pub fn code_action_group(&self) -> bool {
620 self.experimental("codeActionGroup")
621 }
622 pub fn hover_actions(&self) -> bool {
623 self.experimental("hoverActions")
624 }
625 pub fn status_notification(&self) -> bool {
626 self.experimental("statusNotification")
627 }
628}
629
600#[derive(Deserialize)] 630#[derive(Deserialize)]
601#[serde(untagged)] 631#[serde(untagged)]
602enum ManifestOrProjectJson { 632enum 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(
320 acc 320 acc
321 }; 321 };
322 322
323 let res = if snap.config.client_caps.hierarchical_symbols { 323 let res = if snap.config.hierarchical_symbols() {
324 document_symbols.into() 324 document_symbols.into()
325 } else { 325 } else {
326 let url = to_proto::url(&snap, file_id); 326 let url = to_proto::url(&snap, file_id);
@@ -727,7 +727,7 @@ pub(crate) fn handle_folding_range(
727 let folds = snap.analysis.folding_ranges(file_id)?; 727 let folds = snap.analysis.folding_ranges(file_id)?;
728 let text = snap.analysis.file_text(file_id)?; 728 let text = snap.analysis.file_text(file_id)?;
729 let line_index = snap.analysis.file_line_index(file_id)?; 729 let line_index = snap.analysis.file_line_index(file_id)?;
730 let line_folding_only = snap.config.client_caps.line_folding_only; 730 let line_folding_only = snap.config.line_folding_only();
731 let res = folds 731 let res = folds
732 .into_iter() 732 .into_iter()
733 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 733 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -746,11 +746,8 @@ pub(crate) fn handle_signature_help(
746 None => return Ok(None), 746 None => return Ok(None),
747 }; 747 };
748 let concise = !snap.config.call_info_full; 748 let concise = !snap.config.call_info_full;
749 let res = to_proto::signature_help( 749 let res =
750 call_info, 750 to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
751 concise,
752 snap.config.client_caps.signature_help_label_offsets,
753 );
754 Ok(Some(res)) 751 Ok(Some(res))
755} 752}
756 753
@@ -929,7 +926,7 @@ pub(crate) fn handle_code_action(
929 // We intentionally don't support command-based actions, as those either 926 // We intentionally don't support command-based actions, as those either
930 // requires custom client-code anyway, or requires server-initiated edits. 927 // requires custom client-code anyway, or requires server-initiated edits.
931 // Server initiated edits break causality, so we avoid those as well. 928 // Server initiated edits break causality, so we avoid those as well.
932 if !snap.config.client_caps.code_action_literals { 929 if !snap.config.code_action_literals() {
933 return Ok(None); 930 return Ok(None);
934 } 931 }
935 932
@@ -959,7 +956,7 @@ pub(crate) fn handle_code_action(
959 add_quick_fixes(&snap, frange, &line_index, &mut res)?; 956 add_quick_fixes(&snap, frange, &line_index, &mut res)?;
960 } 957 }
961 958
962 if snap.config.client_caps.code_action_resolve { 959 if snap.config.code_action_resolve() {
963 for (index, assist) in 960 for (index, assist) in
964 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() 961 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
965 { 962 {
@@ -1542,7 +1539,7 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1542} 1539}
1543 1540
1544fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { 1541fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1545 let value = if snap.config.client_caps.location_link { 1542 let value = if snap.config.location_link() {
1546 let link = to_proto::location_link(snap, None, nav.clone()).ok()?; 1543 let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1547 to_value(link).ok()? 1544 to_value(link).ok()?
1548 } else { 1545 } else {
@@ -1641,7 +1638,7 @@ fn prepare_hover_actions(
1641 file_id: FileId, 1638 file_id: FileId,
1642 actions: &[HoverAction], 1639 actions: &[HoverAction],
1643) -> Vec<lsp_ext::CommandLinkGroup> { 1640) -> Vec<lsp_ext::CommandLinkGroup> {
1644 if snap.config.hover.none() || !snap.config.client_caps.hover_actions { 1641 if snap.config.hover.none() || !snap.config.hover_actions() {
1645 return Vec::new(); 1642 return Vec::new();
1646 } 1643 }
1647 1644
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 {
46 message: Option<String>, 46 message: Option<String>,
47 fraction: Option<f64>, 47 fraction: Option<f64>,
48 ) { 48 ) {
49 if !self.config.client_caps.work_done_progress { 49 if !self.config.work_done_progress() {
50 return; 50 return;
51 } 51 }
52 let percentage = fraction.map(|f| { 52 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 {
79 } 79 }
80 pub(crate) fn transition(&mut self, new_status: Status) { 80 pub(crate) fn transition(&mut self, new_status: Status) {
81 self.status = new_status; 81 self.status = new_status;
82 if self.config.client_caps.status_notification { 82 if self.config.status_notification() {
83 let lsp_status = match new_status { 83 let lsp_status = match new_status {
84 Status::Loading => lsp_ext::Status::Loading, 84 Status::Loading => lsp_ext::Status::Loading,
85 Status::Ready => lsp_ext::Status::Ready, 85 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(
605 src: Option<FileRange>, 605 src: Option<FileRange>,
606 targets: Vec<NavigationTarget>, 606 targets: Vec<NavigationTarget>,
607) -> Result<lsp_types::GotoDefinitionResponse> { 607) -> Result<lsp_types::GotoDefinitionResponse> {
608 if snap.config.client_caps.location_link { 608 if snap.config.location_link() {
609 let links = targets 609 let links = targets
610 .into_iter() 610 .into_iter()
611 .map(|nav| location_link(snap, src, nav)) 611 .map(|nav| location_link(snap, src, nav))
@@ -785,7 +785,7 @@ pub(crate) fn unresolved_code_action(
785 assert!(assist.source_change.is_none()); 785 assert!(assist.source_change.is_none());
786 let res = lsp_ext::CodeAction { 786 let res = lsp_ext::CodeAction {
787 title: assist.label.to_string(), 787 title: assist.label.to_string(),
788 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 788 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
789 kind: Some(code_action_kind(assist.id.1)), 789 kind: Some(code_action_kind(assist.id.1)),
790 edit: None, 790 edit: None,
791 is_preferred: None, 791 is_preferred: None,
@@ -805,7 +805,7 @@ pub(crate) fn resolved_code_action(
805 let res = lsp_ext::CodeAction { 805 let res = lsp_ext::CodeAction {
806 edit: Some(snippet_workspace_edit(snap, change)?), 806 edit: Some(snippet_workspace_edit(snap, change)?),
807 title: assist.label.to_string(), 807 title: assist.label.to_string(),
808 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 808 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
809 kind: Some(code_action_kind(assist.id.1)), 809 kind: Some(code_action_kind(assist.id.1)),
810 is_preferred: None, 810 is_preferred: None,
811 data: None, 811 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::{
14use lsp_types::{ProgressParams, ProgressParamsValue}; 14use lsp_types::{ProgressParams, ProgressParamsValue};
15use project_model::{CargoConfig, ProjectManifest}; 15use project_model::{CargoConfig, ProjectManifest};
16use rust_analyzer::{ 16use rust_analyzer::{
17 config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, 17 config::{Config, FilesConfig, FilesWatcher, LinkedProject},
18 main_loop, 18 main_loop,
19}; 19};
20use serde::Serialize; 20use serde::Serialize;
@@ -84,10 +84,24 @@ impl<'a> Project<'a> {
84 .collect::<Vec<_>>(); 84 .collect::<Vec<_>>();
85 85
86 let mut config = Config { 86 let mut config = Config {
87 client_caps: ClientCapsConfig { 87 caps: lsp_types::ClientCapabilities {
88 location_link: true, 88 text_document: Some(lsp_types::TextDocumentClientCapabilities {
89 code_action_literals: true, 89 definition: Some(lsp_types::GotoCapability {
90 work_done_progress: true, 90 link_support: Some(true),
91 ..Default::default()
92 }),
93 code_action: Some(lsp_types::CodeActionClientCapabilities {
94 code_action_literal_support: Some(
95 lsp_types::CodeActionLiteralSupport::default(),
96 ),
97 ..Default::default()
98 }),
99 ..Default::default()
100 }),
101 window: Some(lsp_types::WindowClientCapabilities {
102 work_done_progress: Some(true),
103 ..Default::default()
104 }),
91 ..Default::default() 105 ..Default::default()
92 }, 106 },
93 cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, 107 cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() },