diff options
Diffstat (limited to 'helix-lsp/src/client.rs')
-rw-r--r-- | helix-lsp/src/client.rs | 229 |
1 files changed, 168 insertions, 61 deletions
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 424cbabf..2c2c7c88 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -4,7 +4,6 @@ use crate::{ Call, Error, OffsetEncoding, Result, }; -use anyhow::anyhow; use helix_core::{find_root, ChangeSet, Rope}; use lsp_types as lsp; use serde::Deserialize; @@ -546,16 +545,17 @@ impl Client { new_text: &Rope, changes: &ChangeSet, ) -> Option<impl Future<Output = Result<()>>> { - // figure out what kind of sync the server supports - let capabilities = self.capabilities.get().unwrap(); + // Return early if the server does not support document sync. let sync_capabilities = match capabilities.text_document_sync { - Some(lsp::TextDocumentSyncCapability::Kind(kind)) - | Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { - change: Some(kind), - .. - })) => kind, + Some( + lsp::TextDocumentSyncCapability::Kind(kind) + | lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { + change: Some(kind), + .. + }), + ) => kind, // None | SyncOptions { changes: None } _ => return None, }; @@ -631,8 +631,12 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { - // ) -> Result<Vec<lsp::CompletionItem>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support completion. + capabilities.completion_provider.as_ref()?; + let params = lsp::CompletionParams { text_document_position: lsp::TextDocumentPositionParams { text_document, @@ -647,14 +651,25 @@ impl Client { // lsp::CompletionContext { trigger_kind: , trigger_character: Some(), } }; - self.call::<lsp::request::Completion>(params) + Some(self.call::<lsp::request::Completion>(params)) } pub fn resolve_completion_item( &self, completion_item: lsp::CompletionItem, - ) -> impl Future<Output = Result<Value>> { - self.call::<lsp::request::ResolveCompletionItem>(completion_item) + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support resolving completion items. + match capabilities.completion_provider { + Some(lsp::CompletionOptions { + resolve_provider: Some(true), + .. + }) => (), + _ => return None, + } + + Some(self.call::<lsp::request::ResolveCompletionItem>(completion_item)) } pub fn text_document_signature_help( @@ -665,7 +680,7 @@ impl Client { ) -> Option<impl Future<Output = Result<Value>>> { let capabilities = self.capabilities.get().unwrap(); - // Return early if signature help is not supported + // Return early if the server does not support signature help. capabilities.signature_help_provider.as_ref()?; let params = lsp::SignatureHelpParams { @@ -686,7 +701,18 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support hover. + match capabilities.hover_provider { + Some( + lsp::HoverProviderCapability::Simple(true) + | lsp::HoverProviderCapability::Options(_), + ) => (), + _ => return None, + } + let params = lsp::HoverParams { text_document_position_params: lsp::TextDocumentPositionParams { text_document, @@ -696,7 +722,7 @@ impl Client { // lsp::SignatureHelpContext }; - self.call::<lsp::request::HoverRequest>(params) + Some(self.call::<lsp::request::HoverRequest>(params)) } // formatting @@ -709,13 +735,11 @@ impl Client { ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> { let capabilities = self.capabilities.get().unwrap(); - // check if we're able to format + // Return early if the server does not support formatting. match capabilities.document_formatting_provider { - Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (), - // None | Some(false) + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), _ => return None, }; - // TODO: return err::unavailable so we can fall back to tree sitter formatting // merge FormattingOptions with 'config.format' let config_format = self @@ -750,22 +774,20 @@ impl Client { }) } - pub async fn text_document_range_formatting( + pub fn text_document_range_formatting( &self, text_document: lsp::TextDocumentIdentifier, range: lsp::Range, options: lsp::FormattingOptions, work_done_token: Option<lsp::ProgressToken>, - ) -> anyhow::Result<Vec<lsp::TextEdit>> { + ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> { let capabilities = self.capabilities.get().unwrap(); - // check if we're able to format + // Return early if the server does not support range formatting. match capabilities.document_range_formatting_provider { - Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (), - // None | Some(false) - _ => return Ok(Vec::new()), + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, }; - // TODO: return err::unavailable so we can fall back to tree sitter formatting let params = lsp::DocumentRangeFormattingParams { text_document, @@ -774,11 +796,13 @@ impl Client { work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, }; - let response = self - .request::<lsp::request::RangeFormatting>(params) - .await?; + let request = self.call::<lsp::request::RangeFormatting>(params); - Ok(response.unwrap_or_default()) + Some(async move { + let json = request.await?; + let response: Option<Vec<lsp::TextEdit>> = serde_json::from_value(json)?; + Ok(response.unwrap_or_default()) + }) } pub fn text_document_document_highlight( @@ -786,7 +810,15 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support document highlight. + match capabilities.document_highlight_provider { + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, + } + let params = lsp::DocumentHighlightParams { text_document_position_params: lsp::TextDocumentPositionParams { text_document, @@ -798,7 +830,7 @@ impl Client { }, }; - self.call::<lsp::request::DocumentHighlightRequest>(params) + Some(self.call::<lsp::request::DocumentHighlightRequest>(params)) } fn goto_request< @@ -831,8 +863,20 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { - self.goto_request::<lsp::request::GotoDefinition>(text_document, position, work_done_token) + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support goto-definition. + match capabilities.definition_provider { + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, + } + + Some(self.goto_request::<lsp::request::GotoDefinition>( + text_document, + position, + work_done_token, + )) } pub fn goto_type_definition( @@ -840,12 +884,23 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { - self.goto_request::<lsp::request::GotoTypeDefinition>( + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support goto-type-definition. + match capabilities.type_definition_provider { + Some( + lsp::TypeDefinitionProviderCapability::Simple(true) + | lsp::TypeDefinitionProviderCapability::Options(_), + ) => (), + _ => return None, + } + + Some(self.goto_request::<lsp::request::GotoTypeDefinition>( text_document, position, work_done_token, - ) + )) } pub fn goto_implementation( @@ -853,12 +908,23 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { - self.goto_request::<lsp::request::GotoImplementation>( + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support goto-definition. + match capabilities.implementation_provider { + Some( + lsp::ImplementationProviderCapability::Simple(true) + | lsp::ImplementationProviderCapability::Options(_), + ) => (), + _ => return None, + } + + Some(self.goto_request::<lsp::request::GotoImplementation>( text_document, position, work_done_token, - ) + )) } pub fn goto_reference( @@ -866,7 +932,15 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, - ) -> impl Future<Output = Result<Value>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support goto-reference. + match capabilities.references_provider { + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, + } + let params = lsp::ReferenceParams { text_document_position: lsp::TextDocumentPositionParams { text_document, @@ -881,31 +955,47 @@ impl Client { }, }; - self.call::<lsp::request::References>(params) + Some(self.call::<lsp::request::References>(params)) } pub fn document_symbols( &self, text_document: lsp::TextDocumentIdentifier, - ) -> impl Future<Output = Result<Value>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support document symbols. + match capabilities.document_symbol_provider { + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, + } + let params = lsp::DocumentSymbolParams { text_document, work_done_progress_params: lsp::WorkDoneProgressParams::default(), partial_result_params: lsp::PartialResultParams::default(), }; - self.call::<lsp::request::DocumentSymbolRequest>(params) + Some(self.call::<lsp::request::DocumentSymbolRequest>(params)) } // empty string to get all symbols - pub fn workspace_symbols(&self, query: String) -> impl Future<Output = Result<Value>> { + pub fn workspace_symbols(&self, query: String) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support workspace symbols. + match capabilities.workspace_symbol_provider { + Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (), + _ => return None, + } + let params = lsp::WorkspaceSymbolParams { query, work_done_progress_params: lsp::WorkDoneProgressParams::default(), partial_result_params: lsp::PartialResultParams::default(), }; - self.call::<lsp::request::WorkspaceSymbol>(params) + Some(self.call::<lsp::request::WorkspaceSymbol>(params)) } pub fn code_actions( @@ -913,7 +1003,18 @@ impl Client { text_document: lsp::TextDocumentIdentifier, range: lsp::Range, context: lsp::CodeActionContext, - ) -> impl Future<Output = Result<Value>> { + ) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support code actions. + match capabilities.code_action_provider { + Some( + lsp::CodeActionProviderCapability::Simple(true) + | lsp::CodeActionProviderCapability::Options(_), + ) => (), + _ => return None, + } + let params = lsp::CodeActionParams { text_document, range, @@ -922,26 +1023,22 @@ impl Client { partial_result_params: lsp::PartialResultParams::default(), }; - self.call::<lsp::request::CodeActionRequest>(params) + Some(self.call::<lsp::request::CodeActionRequest>(params)) } - pub async fn rename_symbol( + pub fn rename_symbol( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, new_name: String, - ) -> anyhow::Result<lsp::WorkspaceEdit> { + ) -> Option<impl Future<Output = Result<lsp::WorkspaceEdit>>> { let capabilities = self.capabilities.get().unwrap(); - // check if we're able to rename + // Return early if the language server does not support renaming. match capabilities.rename_provider { Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (), // None | Some(false) - _ => { - log::warn!("rename_symbol failed: The server does not support rename"); - let err = "The server does not support rename"; - return Err(anyhow!(err)); - } + _ => return None, }; let params = lsp::RenameParams { @@ -955,11 +1052,21 @@ impl Client { }, }; - let response = self.request::<lsp::request::Rename>(params).await?; - Ok(response.unwrap_or_default()) + let request = self.call::<lsp::request::Rename>(params); + + Some(async move { + let json = request.await?; + let response: Option<lsp::WorkspaceEdit> = serde_json::from_value(json)?; + Ok(response.unwrap_or_default()) + }) } - pub fn command(&self, command: lsp::Command) -> impl Future<Output = Result<Value>> { + pub fn command(&self, command: lsp::Command) -> Option<impl Future<Output = Result<Value>>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the language server does not support executing commands. + capabilities.execute_command_provider.as_ref()?; + let params = lsp::ExecuteCommandParams { command: command.command, arguments: command.arguments.unwrap_or_default(), @@ -968,6 +1075,6 @@ impl Client { }, }; - self.call::<lsp::request::ExecuteCommand>(params) + Some(self.call::<lsp::request::ExecuteCommand>(params)) } } |