Revert "project: Fine-grained language server management" (#23804)

Reverts zed-industries/zed#23708
This commit is contained in:
Piotr Osiewicz 2025-01-28 22:38:06 +01:00 committed by GitHub
parent bda269059b
commit 22afec32cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 935 additions and 2226 deletions

View file

@ -4,10 +4,9 @@ pub use lsp_types::request::*;
pub use lsp_types::*;
use anyhow::{anyhow, Context as _, Result};
use collections::{HashMap, HashSet};
use collections::HashMap;
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, Future, FutureExt};
use gpui::{App, AsyncApp, BackgroundExecutor, SharedString, Task};
use notification::DidChangeWorkspaceFolders;
use parking_lot::{Mutex, RwLock};
use postage::{barrier, prelude::Stream};
use schemars::{
@ -24,11 +23,10 @@ use smol::{
};
use std::{
collections::BTreeSet,
ffi::{OsStr, OsString},
fmt,
io::Write,
ops::{Deref, DerefMut},
ops::DerefMut,
path::PathBuf,
pin::Pin,
sync::{
@ -98,9 +96,9 @@ pub struct LanguageServer {
#[allow(clippy::type_complexity)]
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
output_done_rx: Mutex<Option<barrier::Receiver>>,
root_path: PathBuf,
working_dir: PathBuf,
server: Arc<Mutex<Option<Child>>>,
workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
registered_buffers: Arc<Mutex<HashSet<Url>>>,
}
/// Identifies a running language server.
@ -378,6 +376,8 @@ impl LanguageServer {
Some(stderr),
stderr_capture,
Some(server),
root_path,
working_dir,
code_action_kinds,
binary,
cx,
@ -403,6 +403,8 @@ impl LanguageServer {
stderr: Option<Stderr>,
stderr_capture: Arc<Mutex<Option<String>>>,
server: Option<Child>,
root_path: &Path,
working_dir: &Path,
code_action_kinds: Option<Vec<CodeActionKind>>,
binary: LanguageServerBinary,
cx: AsyncApp,
@ -486,9 +488,9 @@ impl LanguageServer {
executor: cx.background_executor().clone(),
io_tasks: Mutex::new(Some((input_task, output_task))),
output_done_rx: Mutex::new(Some(output_done_rx)),
root_path: root_path.to_path_buf(),
working_dir: working_dir.to_path_buf(),
server: Arc::new(Mutex::new(server)),
workspace_folders: Default::default(),
registered_buffers: Default::default(),
}
}
@ -613,11 +615,12 @@ impl LanguageServer {
}
pub fn default_initialize_params(&self, cx: &App) -> InitializeParams {
let root_uri = Url::from_file_path(&self.working_dir).unwrap();
#[allow(deprecated)]
InitializeParams {
process_id: None,
root_path: None,
root_uri: None,
root_uri: Some(root_uri.clone()),
initialization_options: None,
capabilities: ClientCapabilities {
general: Some(GeneralClientCapabilities {
@ -784,7 +787,10 @@ impl LanguageServer {
}),
},
trace: None,
workspace_folders: None,
workspace_folders: Some(vec![WorkspaceFolder {
uri: root_uri,
name: Default::default(),
}]),
client_info: release_channel::ReleaseChannel::try_global(cx).map(|release_channel| {
ClientInfo {
name: release_channel.display_name().to_string(),
@ -803,10 +809,16 @@ impl LanguageServer {
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize)
pub fn initialize(
mut self,
params: InitializeParams,
initialize_params: Option<InitializeParams>,
configuration: Arc<DidChangeConfigurationParams>,
cx: &App,
) -> Task<Result<Arc<Self>>> {
let params = if let Some(params) = initialize_params {
params
} else {
self.default_initialize_params(cx)
};
cx.spawn(|_| async move {
let response = self.request::<request::Initialize>(params).await?;
if let Some(info) = response.server_info {
@ -1058,10 +1070,16 @@ impl LanguageServer {
self.server_id
}
/// Get the root path of the project the language server is running against.
pub fn root_path(&self) -> &PathBuf {
&self.root_path
}
/// Language server's binary information.
pub fn binary(&self) -> &LanguageServerBinary {
&self.binary
}
/// Sends a RPC request to the language server.
///
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
@ -1189,125 +1207,6 @@ impl LanguageServer {
outbound_tx.try_send(message)?;
Ok(())
}
/// Add new workspace folder to the list.
pub fn add_workspace_folder(&self, uri: Url) {
if self
.capabilities()
.workspace
.and_then(|ws| {
ws.workspace_folders.and_then(|folders| {
folders
.change_notifications
.map(|caps| matches!(caps, OneOf::Left(false)))
})
})
.unwrap_or(true)
{
return;
}
let is_new_folder = self.workspace_folders.lock().insert(uri.clone());
if is_new_folder {
let params = DidChangeWorkspaceFoldersParams {
event: WorkspaceFoldersChangeEvent {
added: vec![WorkspaceFolder {
uri,
name: String::default(),
}],
removed: vec![],
},
};
self.notify::<DidChangeWorkspaceFolders>(&params).log_err();
}
}
/// Add new workspace folder to the list.
pub fn remove_workspace_folder(&self, uri: Url) {
if self
.capabilities()
.workspace
.and_then(|ws| {
ws.workspace_folders.and_then(|folders| {
folders
.change_notifications
.map(|caps| !matches!(caps, OneOf::Left(false)))
})
})
.unwrap_or(true)
{
return;
}
let was_removed = self.workspace_folders.lock().remove(&uri);
if was_removed {
let params = DidChangeWorkspaceFoldersParams {
event: WorkspaceFoldersChangeEvent {
added: vec![],
removed: vec![WorkspaceFolder {
uri,
name: String::default(),
}],
},
};
self.notify::<DidChangeWorkspaceFolders>(&params).log_err();
}
}
pub fn set_workspace_folders(&self, folders: BTreeSet<Url>) {
let mut workspace_folders = self.workspace_folders.lock();
let added: Vec<_> = folders
.iter()
.map(|uri| WorkspaceFolder {
uri: uri.clone(),
name: String::default(),
})
.collect();
let removed: Vec<_> = std::mem::replace(&mut *workspace_folders, folders)
.into_iter()
.map(|uri| WorkspaceFolder {
uri: uri.clone(),
name: String::default(),
})
.collect();
let should_notify = !added.is_empty() || !removed.is_empty();
if should_notify {
let params = DidChangeWorkspaceFoldersParams {
event: WorkspaceFoldersChangeEvent { added, removed },
};
self.notify::<DidChangeWorkspaceFolders>(&params).log_err();
}
}
pub fn workspace_folders(&self) -> impl Deref<Target = BTreeSet<Url>> + '_ {
self.workspace_folders.lock()
}
pub fn register_buffer(
&self,
uri: Url,
language_id: String,
version: i32,
initial_text: String,
) {
let was_already_registered = self.registered_buffers.lock().insert(uri.clone());
if was_already_registered {
self.notify::<notification::DidOpenTextDocument>(&DidOpenTextDocumentParams {
text_document: TextDocumentItem::new(uri, language_id, version, initial_text),
})
.log_err();
}
}
pub fn unregister_buffer(&self, uri: &Url) {
let was_removed = self.registered_buffers.lock().remove(uri);
if was_removed {
self.notify::<notification::DidCloseTextDocument>(&DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier::new(uri.clone()),
})
.log_err();
}
}
}
impl Drop for LanguageServer {
@ -1389,6 +1288,8 @@ impl FakeLanguageServer {
let (stdout_writer, stdout_reader) = async_pipe::pipe();
let (notifications_tx, notifications_rx) = channel::unbounded();
let root = Self::root_path();
let server_name = LanguageServerName(name.clone().into());
let process_name = Arc::from(name.as_str());
let mut server = LanguageServer::new_internal(
@ -1399,6 +1300,8 @@ impl FakeLanguageServer {
None::<async_pipe::PipeReader>,
Arc::new(Mutex::new(None)),
None,
root,
root,
None,
binary.clone(),
cx.clone(),
@ -1416,6 +1319,8 @@ impl FakeLanguageServer {
None::<async_pipe::PipeReader>,
Arc::new(Mutex::new(None)),
None,
root,
root,
None,
binary,
cx.clone(),
@ -1452,6 +1357,16 @@ impl FakeLanguageServer {
(server, fake)
}
#[cfg(target_os = "windows")]
fn root_path() -> &'static Path {
Path::new("C:\\")
}
#[cfg(not(target_os = "windows"))]
fn root_path() -> &'static Path {
Path::new("/")
}
}
#[cfg(any(test, feature = "test-support"))]
@ -1639,14 +1554,12 @@ mod tests {
})
.detach();
let initialize_params = None;
let configuration = DidChangeConfigurationParams {
settings: Default::default(),
};
let server = cx
.update(|cx| {
let params = server.default_initialize_params(cx);
let configuration = DidChangeConfigurationParams {
settings: Default::default(),
};
server.initialize(params, configuration.into(), cx)
})
.update(|cx| server.initialize(initialize_params, configuration.into(), cx))
.await
.unwrap();
server