From 7120d538defbee00681baf7c066ed664d697d9b0 Mon Sep 17 00:00:00 2001 From: suxin2017 Date: Thu, 29 Aug 2024 20:37:08 +0800 Subject: [PATCH] feat(lsp): add codeActionKinds (#3731) --- crates/biome_lsp/src/capabilities.rs | 20 ++++- crates/biome_lsp/tests/server.rs | 106 ++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 4 deletions(-) diff --git a/crates/biome_lsp/src/capabilities.rs b/crates/biome_lsp/src/capabilities.rs index fb59a6c723c9..ecf8771e0319 100644 --- a/crates/biome_lsp/src/capabilities.rs +++ b/crates/biome_lsp/src/capabilities.rs @@ -1,7 +1,8 @@ use crate::converters::{negotiated_encoding, PositionEncoding, WideEncoding}; use tower_lsp::lsp_types::{ - ClientCapabilities, CodeActionProviderCapability, DocumentOnTypeFormattingOptions, OneOf, - PositionEncodingKind, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, + ClientCapabilities, CodeActionKind, CodeActionOptions, CodeActionProviderCapability, + DocumentOnTypeFormattingOptions, OneOf, PositionEncodingKind, ServerCapabilities, + TextDocumentSyncCapability, TextDocumentSyncKind, }; /// The capabilities to send from server as part of [`InitializeResult`] @@ -50,6 +51,19 @@ pub(crate) fn server_capabilities(capabilities: &ClientCapabilities) -> ServerCa } }); + let code_action_provider = capabilities + .text_document + .as_ref() + .and_then(|text_document| text_document.code_action.as_ref()) + .and_then(|code_action| code_action.code_action_literal_support.as_ref()) + .map(|_| { + CodeActionProviderCapability::Options(CodeActionOptions { + code_action_kinds: Some(vec![CodeActionKind::new("source.fixAll.biome")]), + ..Default::default() + }) + }) + .or(Some(CodeActionProviderCapability::Simple(true))); + ServerCapabilities { position_encoding: Some(match negotiated_encoding(capabilities) { PositionEncoding::Utf8 => PositionEncodingKind::UTF8, @@ -64,7 +78,7 @@ pub(crate) fn server_capabilities(capabilities: &ClientCapabilities) -> ServerCa document_formatting_provider: supports_formatter_dynamic_registration, document_range_formatting_provider: supports_range_formatter_dynamic_registration, document_on_type_formatting_provider: supports_on_type_formatter_dynamic_registration, - code_action_provider: Some(CodeActionProviderCapability::Simple(true)), + code_action_provider, rename_provider: None, ..Default::default() } diff --git a/crates/biome_lsp/tests/server.rs b/crates/biome_lsp/tests/server.rs index 7506d1efd575..63ca91a2f2d0 100644 --- a/crates/biome_lsp/tests/server.rs +++ b/crates/biome_lsp/tests/server.rs @@ -28,6 +28,12 @@ use tower::{Service, ServiceExt}; use tower_lsp::jsonrpc; use tower_lsp::jsonrpc::Response; use tower_lsp::lsp_types as lsp; +use tower_lsp::lsp_types::CodeActionClientCapabilities; +use tower_lsp::lsp_types::CodeActionKind; +use tower_lsp::lsp_types::CodeActionKindLiteralSupport; +use tower_lsp::lsp_types::CodeActionLiteralSupport; +use tower_lsp::lsp_types::CodeActionOptions; +use tower_lsp::lsp_types::CodeActionProviderCapability; use tower_lsp::lsp_types::DidOpenTextDocumentParams; use tower_lsp::lsp_types::DocumentFormattingParams; use tower_lsp::lsp_types::FormattingOptions; @@ -36,6 +42,7 @@ use tower_lsp::lsp_types::InitializedParams; use tower_lsp::lsp_types::Position; use tower_lsp::lsp_types::PublishDiagnosticsParams; use tower_lsp::lsp_types::Range; +use tower_lsp::lsp_types::TextDocumentClientCapabilities; use tower_lsp::lsp_types::TextDocumentContentChangeEvent; use tower_lsp::lsp_types::TextDocumentIdentifier; use tower_lsp::lsp_types::TextDocumentItem; @@ -183,7 +190,6 @@ impl Server { ) .await? .context("initialize returned None")?; - Ok(()) } @@ -405,6 +411,104 @@ where Ok(()) } +#[allow(deprecated)] +#[tokio::test] +async fn server_capabilities_fix_all_code_action_kinds() -> Result<()> { + let factory = ServerFactory::default(); + let (service, client) = factory.create(None).into_inner(); + let (stream, sink) = client.split(); + let mut server = Server::new(service); + + let (sender, _) = channel(CHANNEL_BUFFER_SIZE); + let reader = tokio::spawn(client_handler(stream, sink, sender)); + let expect_code_action_provider = + Some(CodeActionProviderCapability::Options(CodeActionOptions { + code_action_kinds: Some(vec![CodeActionKind::new("source.fixAll.biome")]), + ..Default::default() + })); + let res: InitializeResult = server + .request( + "initialize", + "_init", + InitializeParams { + process_id: None, + root_path: None, + root_uri: Some(url!("")), + initialization_options: None, + capabilities: ClientCapabilities { + text_document: Some(TextDocumentClientCapabilities { + code_action: Some(CodeActionClientCapabilities { + code_action_literal_support: Some(CodeActionLiteralSupport { + code_action_kind: CodeActionKindLiteralSupport { + value_set: Vec::new(), + }, + }), + ..CodeActionClientCapabilities::default() + }), + + ..TextDocumentClientCapabilities::default() + }), + + ..ClientCapabilities::default() + }, + trace: None, + workspace_folders: None, + client_info: None, + locale: None, + }, + ) + .await? + .context("initialize returned None")?; + + assert_eq!( + res.capabilities.code_action_provider, + expect_code_action_provider + ); + server.shutdown().await?; + reader.abort(); + + Ok(()) +} + +#[allow(deprecated)] +#[tokio::test] +async fn server_capabilities_default_code_action_kinds() -> Result<()> { + let factory = ServerFactory::default(); + let (service, client) = factory.create(None).into_inner(); + let (stream, sink) = client.split(); + let mut server = Server::new(service); + + let (sender, _) = channel(CHANNEL_BUFFER_SIZE); + let reader = tokio::spawn(client_handler(stream, sink, sender)); + let expect_code_action_provider = Some(CodeActionProviderCapability::Simple(true)); + let res: InitializeResult = server + .request( + "initialize", + "_init", + InitializeParams { + process_id: None, + root_path: None, + root_uri: Some(url!("")), + initialization_options: None, + capabilities: ClientCapabilities::default(), + trace: None, + workspace_folders: None, + client_info: None, + locale: None, + }, + ) + .await? + .context("initialize returned None")?; + assert_eq!( + res.capabilities.code_action_provider, + expect_code_action_provider + ); + server.shutdown().await?; + reader.abort(); + + Ok(()) +} + #[tokio::test] async fn basic_lifecycle() -> Result<()> { let factory = ServerFactory::default();