mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 19:03:30 +04:00
lsp: Check server provider capabilities (#3554)
Language Servers may signal that they do not support a method in the initialization result (server capabilities). We can check these when making LSP requests and hint in the status line when a method is not supported by the server. This can also prevent crashes in servers which assume that clients do not send requests for methods which are disabled in the server capabilities. There is an existing pattern the LSP client module where a method returns `Option<impl Future<Output = Result<_>>>` with `None` signaling no support in the server. This change extends this pattern to the rest of the client functions. And we log an error to the statusline for manually triggered LSP calls which return `None`.
This commit is contained in:
parent
1db01caec7
commit
9059c65a53
@ -4,7 +4,6 @@
|
||||
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 @@ pub fn text_document_did_change(
|
||||
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 {
|
||||
Some(
|
||||
lsp::TextDocumentSyncCapability::Kind(kind)
|
||||
| lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
|
||||
change: Some(kind),
|
||||
..
|
||||
})) => kind,
|
||||
}),
|
||||
) => kind,
|
||||
// None | SyncOptions { changes: None }
|
||||
_ => return None,
|
||||
};
|
||||
@ -631,8 +631,12 @@ pub fn completion(
|
||||
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 @@ pub fn completion(
|
||||
// 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 @@ pub fn text_document_signature_help(
|
||||
) -> 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 @@ pub fn text_document_hover(
|
||||
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 @@ pub fn text_document_hover(
|
||||
// lsp::SignatureHelpContext
|
||||
};
|
||||
|
||||
self.call::<lsp::request::HoverRequest>(params)
|
||||
Some(self.call::<lsp::request::HoverRequest>(params))
|
||||
}
|
||||
|
||||
// formatting
|
||||
@ -709,13 +735,11 @@ pub fn text_document_formatting(
|
||||
) -> 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 @@ pub fn text_document_formatting(
|
||||
})
|
||||
}
|
||||
|
||||
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 @@ pub async fn text_document_range_formatting(
|
||||
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);
|
||||
|
||||
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 @@ pub fn text_document_document_highlight(
|
||||
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 @@ pub fn text_document_document_highlight(
|
||||
},
|
||||
};
|
||||
|
||||
self.call::<lsp::request::DocumentHighlightRequest>(params)
|
||||
Some(self.call::<lsp::request::DocumentHighlightRequest>(params))
|
||||
}
|
||||
|
||||
fn goto_request<
|
||||
@ -831,8 +863,20 @@ pub fn goto_definition(
|
||||
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 @@ pub fn goto_type_definition(
|
||||
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 @@ pub fn goto_implementation(
|
||||
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 @@ pub fn goto_reference(
|
||||
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 @@ pub fn goto_reference(
|
||||
},
|
||||
};
|
||||
|
||||
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 @@ pub fn code_actions(
|
||||
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 @@ pub fn code_actions(
|
||||
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 @@ pub async fn rename_symbol(
|
||||
},
|
||||
};
|
||||
|
||||
let response = self.request::<lsp::request::Rename>(params).await?;
|
||||
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 @@ pub fn command(&self, command: lsp::Command) -> impl Future<Output = Result<Valu
|
||||
},
|
||||
};
|
||||
|
||||
self.call::<lsp::request::ExecuteCommand>(params)
|
||||
Some(self.call::<lsp::request::ExecuteCommand>(params))
|
||||
}
|
||||
}
|
||||
|
@ -3801,15 +3801,21 @@ fn format_selections(cx: &mut Context) {
|
||||
|
||||
let range = ranges[0];
|
||||
|
||||
let edits = tokio::task::block_in_place(|| {
|
||||
helix_lsp::block_on(language_server.text_document_range_formatting(
|
||||
let request = match language_server.text_document_range_formatting(
|
||||
doc.identifier(),
|
||||
range,
|
||||
lsp::FormattingOptions::default(),
|
||||
None,
|
||||
))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support range formatting");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let edits = tokio::task::block_in_place(|| helix_lsp::block_on(request)).unwrap_or_default();
|
||||
|
||||
let transaction = helix_lsp::util::generate_transaction_from_edits(
|
||||
doc.text(),
|
||||
@ -3953,7 +3959,10 @@ pub fn completion(cx: &mut Context) {
|
||||
|
||||
let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
|
||||
|
||||
let future = language_server.completion(doc.identifier(), pos, None);
|
||||
let future = match language_server.completion(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let trigger_offset = cursor;
|
||||
|
||||
|
@ -333,7 +333,14 @@ fn nested_to_flat(
|
||||
let current_url = doc.url();
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
|
||||
let future = language_server.document_symbols(doc.identifier());
|
||||
let future = match language_server.document_symbols(doc.identifier()) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support document symbols");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -365,7 +372,14 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
|
||||
let current_url = doc.url();
|
||||
let language_server = language_server!(cx.editor, doc);
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
let future = language_server.workspace_symbols("".to_string());
|
||||
let future = match language_server.workspace_symbols("".to_string()) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support workspace symbols");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -493,7 +507,7 @@ pub fn code_action(cx: &mut Context) {
|
||||
|
||||
let range = range_to_lsp_range(doc.text(), selection_range, offset_encoding);
|
||||
|
||||
let future = language_server.code_actions(
|
||||
let future = match language_server.code_actions(
|
||||
doc.identifier(),
|
||||
range,
|
||||
// Filter and convert overlapping diagnostics
|
||||
@ -509,7 +523,14 @@ pub fn code_action(cx: &mut Context) {
|
||||
.collect(),
|
||||
only: None,
|
||||
},
|
||||
);
|
||||
) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support code actions");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -617,9 +638,16 @@ pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
|
||||
|
||||
// the command is executed on the server and communicated back
|
||||
// to the client asynchronously using workspace edits
|
||||
let command_future = language_server.command(cmd);
|
||||
let future = match language_server.command(cmd) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
editor.set_error("Language server does not support executing commands");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let res = command_future.await;
|
||||
let res = future.await;
|
||||
|
||||
if let Err(e) = res {
|
||||
log::error!("execute LSP command: {}", e);
|
||||
@ -853,7 +881,14 @@ pub fn goto_definition(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.goto_definition(doc.identifier(), pos, None);
|
||||
let future = match language_server.goto_definition(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support goto-definition");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -871,7 +906,14 @@ pub fn goto_type_definition(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.goto_type_definition(doc.identifier(), pos, None);
|
||||
let future = match language_server.goto_type_definition(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support goto-type-definition");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -889,7 +931,14 @@ pub fn goto_implementation(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.goto_implementation(doc.identifier(), pos, None);
|
||||
let future = match language_server.goto_implementation(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support goto-implementation");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -907,7 +956,14 @@ pub fn goto_reference(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.goto_reference(doc.identifier(), pos, None);
|
||||
let future = match language_server.goto_reference(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support goto-reference");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -950,7 +1006,13 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
|
||||
|
||||
let future = match language_server.text_document_signature_help(doc.identifier(), pos, None) {
|
||||
Some(f) => f,
|
||||
None => return,
|
||||
None => {
|
||||
if was_manually_invoked {
|
||||
cx.editor
|
||||
.set_error("Language server does not support signature-help");
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
@ -1051,7 +1113,14 @@ pub fn hover(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.text_document_hover(doc.identifier(), pos, None);
|
||||
let future = match language_server.text_document_hover(doc.identifier(), pos, None) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support hover");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
@ -1121,8 +1190,16 @@ pub fn rename_symbol(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let task = language_server.rename_symbol(doc.identifier(), pos, input.to_string());
|
||||
match block_on(task) {
|
||||
let future =
|
||||
match language_server.rename_symbol(doc.identifier(), pos, input.to_string()) {
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support symbol renaming");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match block_on(future) {
|
||||
Ok(edits) => apply_workspace_edit(cx.editor, offset_encoding, &edits),
|
||||
Err(err) => cx.editor.set_error(err.to_string()),
|
||||
}
|
||||
@ -1137,7 +1214,15 @@ pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
|
||||
|
||||
let pos = doc.position(view.id, offset_encoding);
|
||||
|
||||
let future = language_server.text_document_document_highlight(doc.identifier(), pos, None);
|
||||
let future = match language_server.text_document_document_highlight(doc.identifier(), pos, None)
|
||||
{
|
||||
Some(future) => future,
|
||||
None => {
|
||||
cx.editor
|
||||
.set_error("Language server does not support document highlight");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
|
@ -245,21 +245,13 @@ fn resolve_completion_item(
|
||||
completion_item: lsp::CompletionItem,
|
||||
) -> Option<CompletionItem> {
|
||||
let language_server = doc.language_server()?;
|
||||
let completion_resolve_provider = language_server
|
||||
.capabilities()
|
||||
.completion_provider
|
||||
.as_ref()?
|
||||
.resolve_provider;
|
||||
if completion_resolve_provider != Some(true) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let future = language_server.resolve_completion_item(completion_item);
|
||||
let future = language_server.resolve_completion_item(completion_item)?;
|
||||
let response = helix_lsp::block_on(future);
|
||||
match response {
|
||||
Ok(value) => serde_json::from_value(value).ok(),
|
||||
Err(err) => {
|
||||
log::error!("execute LSP command: {}", err);
|
||||
log::error!("Failed to resolve completion item: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -330,7 +322,10 @@ pub fn ensure_item_resolved(&mut self, cx: &mut commands::Context) -> bool {
|
||||
};
|
||||
|
||||
// This method should not block the compositor so we handle the response asynchronously.
|
||||
let future = language_server.resolve_completion_item(current_item.clone());
|
||||
let future = match language_server.resolve_completion_item(current_item.clone()) {
|
||||
Some(future) => future,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
cx.callback(
|
||||
future,
|
||||
|
Loading…
Reference in New Issue
Block a user