fix(lsp): send error response to request when no data is available

Sending a response with both `error` and `result` set to `None` does not
conform to the LSP spec, and results in an error being displayed by
Neovim's client.
This commit is contained in:
Will Lillis 2025-09-07 00:37:35 -04:00
parent ba39d01552
commit 263d86dcef
2 changed files with 20 additions and 42 deletions

View file

@ -81,13 +81,7 @@ pub fn handle_request(
GotoDefinition::METHOD => {
let (id, params) =
cast_req::<GotoDefinition>(req).expect("Failed to cast completion request");
handle_goto_def_request(
connection,
id,
&params,
config.get_config(&params.text_document_position_params.text_document.uri),
doc_store,
)?;
handle_goto_def_request(connection, id, &params, doc_store)?;
info!(
"{} request serviced in {}ms",
GotoDefinition::METHOD,
@ -97,13 +91,7 @@ pub fn handle_request(
DocumentSymbolRequest::METHOD => {
let (id, params) =
cast_req::<DocumentSymbolRequest>(req).expect("Failed to cast completion request");
handle_document_symbols_request(
connection,
id,
&params,
config.get_config(&params.text_document.uri),
doc_store,
)?;
handle_document_symbols_request(connection, id, &params, doc_store)?;
info!(
"{} request serviced in {}ms",
DocumentSymbolRequest::METHOD,
@ -130,13 +118,7 @@ pub fn handle_request(
References::METHOD => {
let (id, params) =
cast_req::<References>(req).expect("Failed to cast completion request");
handle_references_request(
connection,
id,
&params,
config.get_config(&params.text_document_position.text_document.uri),
doc_store,
)?;
handle_references_request(connection, id, &params, doc_store)?;
info!(
"{} request serviced in {}ms",
References::METHOD,
@ -305,7 +287,7 @@ pub fn handle_hover_request(
{
get_word_from_pos_params(doc, &params.text_document_position_params)
} else {
return send_empty_resp(connection, id, config);
return send_empty_resp(connection, id);
};
// needed to appease the borrow checker, since `word` is a reference to owned
@ -322,7 +304,7 @@ pub fn handle_hover_request(
return Ok(connection.sender.send(Message::Response(result))?);
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Handles completion requests
@ -363,7 +345,7 @@ pub fn handle_completion_request(
}
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Handles go to definition requests
@ -379,7 +361,6 @@ pub fn handle_goto_def_request(
connection: &Connection,
id: RequestId,
params: &GotoDefinitionParams,
config: &Config,
doc_store: &mut DocumentStore,
) -> Result<()> {
let uri = &params.text_document_position_params.text_document.uri;
@ -398,7 +379,7 @@ pub fn handle_goto_def_request(
}
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Handles document symbols requests
@ -414,7 +395,6 @@ pub fn handle_document_symbols_request(
connection: &Connection,
id: RequestId,
params: &DocumentSymbolParams,
config: &Config,
doc_store: &mut DocumentStore,
) -> Result<()> {
let uri = &params.text_document.uri;
@ -433,7 +413,7 @@ pub fn handle_document_symbols_request(
}
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Handles signature help requests
@ -477,7 +457,7 @@ pub fn handle_signature_help_request(
}
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Handles reference requests
@ -493,7 +473,6 @@ pub fn handle_references_request(
connection: &Connection,
id: RequestId,
params: &ReferenceParams,
config: &Config,
doc_store: &mut DocumentStore,
) -> Result<()> {
let uri = &params.text_document_position.text_document.uri;
@ -513,7 +492,7 @@ pub fn handle_references_request(
}
}
send_empty_resp(connection, id, config)
send_empty_resp(connection, id)
}
/// Produces diagnostics and sends a `PublishDiagnostics` notification to the client

View file

@ -35,7 +35,7 @@ use tree_sitter::InputEdit;
use crate::{
Arch, ArchOrAssembler, Assembler, Completable, CompletionItems, Config, ConfigOptions,
Directive, DocumentStore, Hoverable, Instruction, LspClient, NameToInstructionMap, RootConfig,
Directive, DocumentStore, Hoverable, Instruction, NameToInstructionMap, RootConfig,
ServerStore, TreeEntry, types::Column, ustr,
};
@ -67,25 +67,24 @@ pub fn run_info() {
}
}
/// Sends an empty, non-error response to the lsp client via `connection`
/// Sends an response indicating no information was available to
/// the lsp client via `connection`
///
/// # Errors
///
/// Returns `Err` if the response fails to send via `connection`
pub fn send_empty_resp(connection: &Connection, id: RequestId, config: &Config) -> Result<()> {
pub fn send_empty_resp(connection: &Connection, id: RequestId) -> Result<()> {
let empty_resp = Response {
id,
result: None,
error: None,
error: Some(lsp_server::ResponseError {
code: lsp_server::ErrorCode::RequestFailed as i32,
message: "No information available".to_string(),
data: None,
}),
};
// Helix shuts the server down when the above empty response is sent,
// so send nothing in its case
if config.client == Some(LspClient::Helix) {
Ok(())
} else {
Ok(connection.sender.send(Message::Response(empty_resp))?)
}
Ok(connection.sender.send(Message::Response(empty_resp))?)
}
/// Sends a notification with to the client