fix(lsp): exit on null root_dir (#15051)

# Description

This PR fixes one reported bug of recent lsp changes.

It exit unexpectedly with empty `root_dir` settings in neovim.

# User-Facing Changes

# Tests + Formatting

+1 test case

# After Submitting
This commit is contained in:
zc he 2025-02-08 20:49:38 +08:00 committed by GitHub
parent 2891867de9
commit 9fa2f43d06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 43 deletions

View File

@ -151,7 +151,7 @@ impl LanguageServer {
!self.initial_engine_state.signals().interrupted() !self.initial_engine_state.signals().interrupted()
}) })
.into_diagnostic()?; .into_diagnostic()?;
self.initialize_workspace_folders(init_params)?; self.initialize_workspace_folders(init_params);
while !self.initial_engine_state.signals().interrupted() { while !self.initial_engine_state.signals().interrupted() {
// first check new messages from child thread // first check new messages from child thread
@ -730,7 +730,7 @@ mod tests {
DidChangeTextDocument, DidOpenTextDocument, Exit, Initialized, Notification, DidChangeTextDocument, DidOpenTextDocument, Exit, Initialized, Notification,
}, },
request::{Completion, HoverRequest, Initialize, Request, Shutdown}, request::{Completion, HoverRequest, Initialize, Request, Shutdown},
CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams, InitializeParams, CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
InitializedParams, PartialResultParams, Position, TextDocumentContentChangeEvent, InitializedParams, PartialResultParams, Position, TextDocumentContentChangeEvent,
TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams,
WorkDoneProgressParams, WorkDoneProgressParams,
@ -740,7 +740,7 @@ mod tests {
use std::time::Duration; use std::time::Duration;
pub(crate) fn initialize_language_server( pub(crate) fn initialize_language_server(
params: Option<InitializeParams>, params: Option<serde_json::Value>,
) -> (Connection, Receiver<Result<()>>) { ) -> (Connection, Receiver<Result<()>>) {
use std::sync::mpsc; use std::sync::mpsc;
let (client_connection, server_connection) = Connection::memory(); let (client_connection, server_connection) = Connection::memory();
@ -757,7 +757,7 @@ mod tests {
.send(Message::Request(lsp_server::Request { .send(Message::Request(lsp_server::Request {
id: 1.into(), id: 1.into(),
method: Initialize::METHOD.to_string(), method: Initialize::METHOD.to_string(),
params: serde_json::to_value(params.unwrap_or_default()).unwrap(), params: params.unwrap_or(serde_json::Value::Null),
})) }))
.unwrap(); .unwrap();
client_connection client_connection

View File

@ -50,15 +50,14 @@ impl LanguageServer {
pub(crate) fn initialize_workspace_folders( pub(crate) fn initialize_workspace_folders(
&mut self, &mut self,
init_params: serde_json::Value, init_params: serde_json::Value,
) -> Result<()> { ) -> Option<()> {
if let Some(array) = init_params.get("workspaceFolders") { if let Some(array) = init_params.get("workspaceFolders") {
let folders: Vec<WorkspaceFolder> = let folders: Vec<WorkspaceFolder> = serde_json::from_value(array.clone()).ok()?;
serde_json::from_value(array.clone()).into_diagnostic()?;
for folder in folders { for folder in folders {
self.workspace_folders.insert(folder.name.clone(), folder); self.workspace_folders.insert(folder.name.clone(), folder);
} }
} }
Ok(()) Some(())
} }
/// Highlight all occurrences of the text at cursor, in current file /// Highlight all occurrences of the text at cursor, in current file
@ -568,18 +567,36 @@ mod tests {
.unwrap() .unwrap()
} }
/// Should not exit on malformed init_params
#[test]
fn malformed_init_params() {
let mut script = fixtures();
script.push("lsp");
script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(
serde_json::json!({ "workspaceFolders": serde_json::Value::Null }),
));
script.push("foo.nu");
let script = path_to_uri(&script);
open_unchecked(&client_connection, script.clone());
}
#[test] #[test]
fn command_reference_in_workspace() { fn command_reference_in_workspace() {
let mut script = fixtures(); let mut script = fixtures();
script.push("lsp"); script.push("lsp");
script.push("workspace"); script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(InitializeParams { let (client_connection, _recv) = initialize_language_server(
workspace_folders: Some(vec![WorkspaceFolder { serde_json::to_value(InitializeParams {
uri: path_to_uri(&script), workspace_folders: Some(vec![WorkspaceFolder {
name: "random name".to_string(), uri: path_to_uri(&script),
}]), name: "random name".to_string(),
..Default::default() }]),
})); ..Default::default()
})
.ok(),
);
script.push("foo.nu"); script.push("foo.nu");
let script = path_to_uri(&script); let script = path_to_uri(&script);
@ -620,13 +637,16 @@ mod tests {
let mut script = fixtures(); let mut script = fixtures();
script.push("lsp"); script.push("lsp");
script.push("workspace"); script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(InitializeParams { let (client_connection, _recv) = initialize_language_server(
workspace_folders: Some(vec![WorkspaceFolder { serde_json::to_value(InitializeParams {
uri: path_to_uri(&script), workspace_folders: Some(vec![WorkspaceFolder {
name: "random name".to_string(), uri: path_to_uri(&script),
}]), name: "random name".to_string(),
..Default::default() }]),
})); ..Default::default()
})
.ok(),
);
script.push("foo.nu"); script.push("foo.nu");
let script = path_to_uri(&script); let script = path_to_uri(&script);
@ -667,13 +687,16 @@ mod tests {
let mut script = fixtures(); let mut script = fixtures();
script.push("lsp"); script.push("lsp");
script.push("workspace"); script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(InitializeParams { let (client_connection, _recv) = initialize_language_server(
workspace_folders: Some(vec![WorkspaceFolder { serde_json::to_value(InitializeParams {
uri: path_to_uri(&script), workspace_folders: Some(vec![WorkspaceFolder {
name: "random name".to_string(), uri: path_to_uri(&script),
}]), name: "random name".to_string(),
..Default::default() }]),
})); ..Default::default()
})
.ok(),
);
script.push("foo.nu"); script.push("foo.nu");
let script = path_to_uri(&script); let script = path_to_uri(&script);
@ -742,13 +765,16 @@ mod tests {
let mut script = fixtures(); let mut script = fixtures();
script.push("lsp"); script.push("lsp");
script.push("workspace"); script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(InitializeParams { let (client_connection, _recv) = initialize_language_server(
workspace_folders: Some(vec![WorkspaceFolder { serde_json::to_value(InitializeParams {
uri: path_to_uri(&script), workspace_folders: Some(vec![WorkspaceFolder {
name: "random name".to_string(), uri: path_to_uri(&script),
}]), name: "random name".to_string(),
..Default::default() }]),
})); ..Default::default()
})
.ok(),
);
script.push("foo.nu"); script.push("foo.nu");
let script = path_to_uri(&script); let script = path_to_uri(&script);
@ -807,13 +833,16 @@ mod tests {
let mut script = fixtures(); let mut script = fixtures();
script.push("lsp"); script.push("lsp");
script.push("workspace"); script.push("workspace");
let (client_connection, _recv) = initialize_language_server(Some(InitializeParams { let (client_connection, _recv) = initialize_language_server(
workspace_folders: Some(vec![WorkspaceFolder { serde_json::to_value(InitializeParams {
uri: path_to_uri(&script), workspace_folders: Some(vec![WorkspaceFolder {
name: "random name".to_string(), uri: path_to_uri(&script),
}]), name: "random name".to_string(),
..Default::default() }]),
})); ..Default::default()
})
.ok(),
);
script.push("foo.nu"); script.push("foo.nu");
let script = path_to_uri(&script); let script = path_to_uri(&script);