From 594ce72d1e37dadb23c15b6daf18714200bfd191 Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 20 Jul 2020 22:04:59 +0300 Subject: [PATCH 1/4] Prepare server capabilities based on client ones. --- crates/rust-analyzer/src/caps.rs | 112 ++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 37d6954484..34fefe0346 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -7,16 +7,18 @@ use lsp_types::{ DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend, - SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, - TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, - WorkDoneProgressOptions, + SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities, + SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, + TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, }; -use serde_json::json; +use serde_json::{json, Value}; use crate::semantic_tokens; pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { let code_action_provider = code_action_capabilities(client_caps); + let semantic_tokens_provider = semantic_tokens_capabilities(client_caps); + let experimental = experimental_capabilities(client_caps); ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { @@ -69,50 +71,84 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti execute_command_provider: None, workspace: None, call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), - semantic_tokens_provider: Some( - SemanticTokensOptions { - legend: SemanticTokensLegend { - token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(), - token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), - }, - - document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), - range_provider: Some(true), - work_done_progress_options: Default::default(), - } - .into(), - ), - experimental: Some(json!({ - "joinLines": true, - "ssr": true, - "onEnter": true, - "parentModule": true, - "runnables": { - "kinds": [ "cargo" ], - }, - })), + semantic_tokens_provider, + experimental, } } +fn experimental_capabilities(client_caps: &ClientCapabilities) -> Option { + client_caps.experimental.as_ref().and_then(|it| { + it.as_object().map(|map| { + let mut obj = json!({}); + let result = obj.as_object_mut().unwrap(); + + if map.contains_key("joinLines") { + result.insert("joinLines".into(), true.into()); + } + + if map.contains_key("ssr") { + result.insert("ssr".into(), true.into()); + } + + if map.contains_key("onEnter") { + result.insert("onEnter".into(), true.into()); + } + + if map.contains_key("parentModule") { + result.insert("parentModule".into(), true.into()); + } + + if map.contains_key("runnables") { + result.insert("runnables".into(), json!({ "kinds": [ "cargo" ] })); + } + + obj + }) + }) +} + +fn semantic_tokens_capabilities( + client_caps: &ClientCapabilities, +) -> Option { + client_caps.text_document.as_ref().and_then(|it| it.semantic_tokens.as_ref()).map(|_| + // client supports semanticTokens + SemanticTokensOptions { + legend: SemanticTokensLegend { + token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(), + token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), + }, + + document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), + range_provider: Some(true), + work_done_progress_options: Default::default(), + } + .into()) +} + fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability { client_caps .text_document .as_ref() .and_then(|it| it.code_action.as_ref()) .and_then(|it| it.code_action_literal_support.as_ref()) - .map_or(CodeActionProviderCapability::Simple(true), |_| { + .map_or(CodeActionProviderCapability::Simple(true), |caps| { + let mut action_kinds = vec![ + CodeActionKind::EMPTY, + CodeActionKind::QUICKFIX, + CodeActionKind::REFACTOR, + CodeActionKind::REFACTOR_EXTRACT, + CodeActionKind::REFACTOR_INLINE, + CodeActionKind::REFACTOR_REWRITE, + ]; + + // Not all clients can fall back gracefully for unknown values. + // Microsoft.VisualStudio.LanguageServer.Protocol.CodeActionKind does not support CodeActionKind::EMPTY + // So have to filter out. + action_kinds + .retain(|it| caps.code_action_kind.value_set.contains(&it.as_str().to_owned())); + CodeActionProviderCapability::Options(CodeActionOptions { - // Advertise support for all built-in CodeActionKinds. - // Ideally we would base this off of the client capabilities - // but the client is supposed to fall back gracefully for unknown values. - code_action_kinds: Some(vec![ - CodeActionKind::EMPTY, - CodeActionKind::QUICKFIX, - CodeActionKind::REFACTOR, - CodeActionKind::REFACTOR_EXTRACT, - CodeActionKind::REFACTOR_INLINE, - CodeActionKind::REFACTOR_REWRITE, - ]), + code_action_kinds: Some(action_kinds), work_done_progress_options: Default::default(), }) }) From f195f876c38e5e013c095c494ec98be465c806db Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 24 Jul 2020 10:41:50 +0300 Subject: [PATCH 2/4] Send dynamic didSave only if the client supports --- crates/rust-analyzer/src/config.rs | 7 +++ crates/rust-analyzer/src/main_loop.rs | 65 ++++++++++++++------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 8947ccf07e..93605314a4 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -128,6 +128,7 @@ pub struct ClientCapsConfig { pub hover_actions: bool, pub status_notification: bool, pub signature_help_label_offsets: bool, + pub dynamic_watched_files: bool, } impl Config { @@ -290,6 +291,12 @@ impl Config { } pub fn update_caps(&mut self, caps: &ClientCapabilities) { + if let Some(ws_caps) = caps.workspace.as_ref() { + if let Some(did_change_watched_files) = ws_caps.did_change_watched_files.as_ref() { + self.client_caps.dynamic_watched_files = did_change_watched_files.dynamic_registration.unwrap_or(false); + } + } + if let Some(doc_caps) = caps.text_document.as_ref() { if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { self.client_caps.location_link = value; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e95d4157c0..b44fd9eb4f 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -106,38 +106,41 @@ impl GlobalState { ); }; - let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { - include_text: Some(false), - text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { - document_selector: Some(vec![ - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/*.rs".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.toml".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.lock".into()), - }, - ]), - }, - }; + if self.config.client_caps.dynamic_watched_files { + let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { + include_text: Some(false), + text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { + document_selector: Some(vec![ + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/*.rs".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.toml".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.lock".into()), + }, + ]), + }, + }; - let registration = lsp_types::Registration { - id: "textDocument/didSave".to_string(), - method: "textDocument/didSave".to_string(), - register_options: Some(serde_json::to_value(save_registration_options).unwrap()), - }; - self.send_request::( - lsp_types::RegistrationParams { registrations: vec![registration] }, - |_, _| (), - ); + let registration = lsp_types::Registration { + id: "textDocument/didSave".to_string(), + method: "textDocument/didSave".to_string(), + register_options: Some(serde_json::to_value(save_registration_options).unwrap()), + }; + + self.send_request::( + lsp_types::RegistrationParams { registrations: vec![registration] }, + |_, _| (), + ); + } self.fetch_workspaces(); From 57b4ec4d5eaf56337f2d3080b02d93404dcb9dc3 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 24 Jul 2020 13:17:32 +0300 Subject: [PATCH 3/4] Code formatting --- crates/rust-analyzer/src/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 93605314a4..ad62571eff 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -293,7 +293,8 @@ impl Config { pub fn update_caps(&mut self, caps: &ClientCapabilities) { if let Some(ws_caps) = caps.workspace.as_ref() { if let Some(did_change_watched_files) = ws_caps.did_change_watched_files.as_ref() { - self.client_caps.dynamic_watched_files = did_change_watched_files.dynamic_registration.unwrap_or(false); + self.client_caps.dynamic_watched_files = + did_change_watched_files.dynamic_registration.unwrap_or(false); } } From a84dc4425a05f0cdb1edefcd77de008940824372 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 24 Jul 2020 16:26:50 +0300 Subject: [PATCH 4/4] Always send experimental & semantic server caps. --- crates/rust-analyzer/src/caps.rs | 110 +++++++++++-------------------- 1 file changed, 37 insertions(+), 73 deletions(-) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 34fefe0346..37d6954484 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -7,18 +7,16 @@ use lsp_types::{ DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend, - SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities, - SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, - TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, + SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, + TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, + WorkDoneProgressOptions, }; -use serde_json::{json, Value}; +use serde_json::json; use crate::semantic_tokens; pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { let code_action_provider = code_action_capabilities(client_caps); - let semantic_tokens_provider = semantic_tokens_capabilities(client_caps); - let experimental = experimental_capabilities(client_caps); ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { @@ -71,58 +69,29 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti execute_command_provider: None, workspace: None, call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), - semantic_tokens_provider, - experimental, - } -} - -fn experimental_capabilities(client_caps: &ClientCapabilities) -> Option { - client_caps.experimental.as_ref().and_then(|it| { - it.as_object().map(|map| { - let mut obj = json!({}); - let result = obj.as_object_mut().unwrap(); - - if map.contains_key("joinLines") { - result.insert("joinLines".into(), true.into()); - } - - if map.contains_key("ssr") { - result.insert("ssr".into(), true.into()); - } - - if map.contains_key("onEnter") { - result.insert("onEnter".into(), true.into()); - } - - if map.contains_key("parentModule") { - result.insert("parentModule".into(), true.into()); - } - - if map.contains_key("runnables") { - result.insert("runnables".into(), json!({ "kinds": [ "cargo" ] })); - } - - obj - }) - }) -} - -fn semantic_tokens_capabilities( - client_caps: &ClientCapabilities, -) -> Option { - client_caps.text_document.as_ref().and_then(|it| it.semantic_tokens.as_ref()).map(|_| - // client supports semanticTokens + semantic_tokens_provider: Some( SemanticTokensOptions { - legend: SemanticTokensLegend { - token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(), - token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), - }, + legend: SemanticTokensLegend { + token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(), + token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), + }, - document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), - range_provider: Some(true), - work_done_progress_options: Default::default(), - } - .into()) + document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), + range_provider: Some(true), + work_done_progress_options: Default::default(), + } + .into(), + ), + experimental: Some(json!({ + "joinLines": true, + "ssr": true, + "onEnter": true, + "parentModule": true, + "runnables": { + "kinds": [ "cargo" ], + }, + })), + } } fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability { @@ -131,24 +100,19 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi .as_ref() .and_then(|it| it.code_action.as_ref()) .and_then(|it| it.code_action_literal_support.as_ref()) - .map_or(CodeActionProviderCapability::Simple(true), |caps| { - let mut action_kinds = vec![ - CodeActionKind::EMPTY, - CodeActionKind::QUICKFIX, - CodeActionKind::REFACTOR, - CodeActionKind::REFACTOR_EXTRACT, - CodeActionKind::REFACTOR_INLINE, - CodeActionKind::REFACTOR_REWRITE, - ]; - - // Not all clients can fall back gracefully for unknown values. - // Microsoft.VisualStudio.LanguageServer.Protocol.CodeActionKind does not support CodeActionKind::EMPTY - // So have to filter out. - action_kinds - .retain(|it| caps.code_action_kind.value_set.contains(&it.as_str().to_owned())); - + .map_or(CodeActionProviderCapability::Simple(true), |_| { CodeActionProviderCapability::Options(CodeActionOptions { - code_action_kinds: Some(action_kinds), + // Advertise support for all built-in CodeActionKinds. + // Ideally we would base this off of the client capabilities + // but the client is supposed to fall back gracefully for unknown values. + code_action_kinds: Some(vec![ + CodeActionKind::EMPTY, + CodeActionKind::QUICKFIX, + CodeActionKind::REFACTOR, + CodeActionKind::REFACTOR_EXTRACT, + CodeActionKind::REFACTOR_INLINE, + CodeActionKind::REFACTOR_REWRITE, + ]), work_done_progress_options: Default::default(), }) })