4516: LSP: Two stage initialization r=kjeremy a=kjeremy

Fills in server information.

Derives CodeAction capabilities from the client. If code action literals
are unsupported we fall back to the "simple support" which just sends back
commands (this is already supported in our config). The difference being
that we did not adjust our server capabilities so that if the client was
checking for `CodeActionProvider: "true"` in the response that would have failed.

Part of #144
Fixes #4130 (the specific case called out in that issue)

Co-authored-by: kjeremy <kjeremy@gmail.com>
This commit is contained in:
bors[bot] 2020-05-22 13:12:57 +00:00 committed by GitHub
commit 5aa3a4c04f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 26 deletions

4
Cargo.lock generated
View File

@ -658,9 +658,9 @@ dependencies = [
[[package]]
name = "lsp-server"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5383e043329615624bbf45e1ba27bd75c176762b2592855c659bc28ac580a06b"
checksum = "dccec31bfd027ac0dd288a78e19005fd89624d9099456e284b5241316a6c3072"
dependencies = [
"crossbeam-channel",
"log",

View File

@ -74,12 +74,25 @@ fn run_server() -> Result<()> {
log::info!("lifecycle: server started");
let (connection, io_threads) = Connection::stdio();
let server_capabilities = serde_json::to_value(rust_analyzer::server_capabilities()).unwrap();
let initialize_params = connection.initialize(server_capabilities)?;
let (initialize_id, initialize_params) = connection.initialize_start()?;
let initialize_params =
from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
let initialize_result = lsp_types::InitializeResult {
capabilities: server_capabilities,
server_info: Some(lsp_types::ServerInfo {
name: String::from("rust-analyzer"),
version: Some(String::from(env!("REV"))),
}),
};
let initialize_result = serde_json::to_value(initialize_result).unwrap();
connection.initialize_finish(initialize_id, initialize_result)?;
if let Some(client_info) = initialize_params.client_info {
log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
}

View File

@ -2,19 +2,22 @@
use std::env;
use lsp_types::{
CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability,
CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions,
FoldingRangeProviderCapability, ImplementationProviderCapability, RenameOptions,
RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability,
SemanticTokensDocumentProvider, SemanticTokensLegend, SemanticTokensOptions,
ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
CallHierarchyServerCapability, ClientCapabilities, CodeActionOptions,
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
WorkDoneProgressOptions,
};
use serde_json::json;
use crate::semantic_tokens;
pub fn server_capabilities() -> ServerCapabilities {
pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
let code_action_provider = code_action_capabilities(client_caps);
ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
@ -46,20 +49,7 @@ pub fn server_capabilities() -> ServerCapabilities {
document_highlight_provider: Some(true),
document_symbol_provider: Some(true),
workspace_symbol_provider: Some(true),
code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
// Advertise support for all built-in CodeActionKinds
code_action_kinds: Some(vec![
lsp_types::code_action_kind::EMPTY.to_string(),
lsp_types::code_action_kind::QUICKFIX.to_string(),
lsp_types::code_action_kind::REFACTOR.to_string(),
lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
lsp_types::code_action_kind::SOURCE.to_string(),
lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
]),
work_done_progress_options: Default::default(),
})),
code_action_provider: Some(code_action_provider),
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
document_formatting_provider: Some(true),
document_range_formatting_provider: None,
@ -98,3 +88,29 @@ pub fn server_capabilities() -> ServerCapabilities {
})),
}
}
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), |_| {
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![
lsp_types::code_action_kind::EMPTY.to_string(),
lsp_types::code_action_kind::QUICKFIX.to_string(),
lsp_types::code_action_kind::REFACTOR.to_string(),
lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
lsp_types::code_action_kind::SOURCE.to_string(),
lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
]),
work_done_progress_options: Default::default(),
})
})
}