lsp: Remove reinstall, update config (#18318)

Release Notes:

- Fixed overriding the path of a language server binary for all language
servers. `{"lsp":{"<lsp-name>":{"binary":{"path": "_"}}}}` will now work
for all language servers including those defined by extensions.
- (breaking change) To disable finding lsp adapters in your path, you
must now specify
`{"lsp":{"<lsp-name>":{"binary":{"ignore_system_version": true}}}}`.
Previously this was `{"lsp":{"<lsp-name>":{"binary":{"path_lookup":
false}}}}`. Note that this setting still does not apply to extensions.
- Removed automatic reinstallation of language servers. (It mostly
didn't work)

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
Conrad Irwin 2024-09-25 11:45:56 -06:00 committed by GitHub
parent 1f54fde4d2
commit dc48af0ca1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 405 additions and 940 deletions

View file

@ -4,18 +4,17 @@ use crate::{
},
task_context::ContextProvider,
with_parser, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
LanguageServerName, LspAdapter, LspAdapterDelegate, PLAIN_TEXT,
LanguageServerName, LspAdapter, PLAIN_TEXT,
};
use anyhow::{anyhow, Context, Result};
use collections::{hash_map, HashMap, HashSet};
use futures::{
channel::{mpsc, oneshot},
future::Shared,
Future,
};
use globset::GlobSet;
use gpui::{AppContext, BackgroundExecutor, Task};
use gpui::{AppContext, BackgroundExecutor};
use lsp::LanguageServerId;
use parking_lot::{Mutex, RwLock};
use postage::watch;
@ -118,12 +117,6 @@ pub enum LanguageServerBinaryStatus {
Failed { error: String },
}
pub struct PendingLanguageServer {
pub server_id: LanguageServerId,
pub task: Task<Result<(lsp::LanguageServer, Option<serde_json::Value>)>>,
pub container_dir: Option<Arc<Path>>,
}
#[derive(Clone)]
pub struct AvailableLanguage {
id: LanguageId,
@ -882,123 +875,53 @@ impl LanguageRegistry {
self.lsp_binary_status_tx.send(server_name, status);
}
#[allow(clippy::too_many_arguments)]
pub fn create_pending_language_server(
self: &Arc<Self>,
stderr_capture: Arc<Mutex<Option<String>>>,
_language_name_for_tests: LanguageName,
adapter: Arc<CachedLspAdapter>,
root_path: Arc<Path>,
delegate: Arc<dyn LspAdapterDelegate>,
project_environment: Shared<Task<Option<HashMap<String, String>>>>,
cx: &mut AppContext,
) -> Option<PendingLanguageServer> {
let server_id = self.state.write().next_language_server_id();
log::info!(
"attempting to start language server {:?}, path: {root_path:?}, id: {server_id}",
adapter.name.0
);
pub fn next_language_server_id(&self) -> LanguageServerId {
self.state.write().next_language_server_id()
}
let container_dir: Option<Arc<Path>> = self
.language_server_download_dir
pub fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
self.language_server_download_dir
.as_ref()
.map(|dir| Arc::from(dir.join(adapter.name.0.as_ref())));
let root_path = root_path.clone();
let this = Arc::downgrade(self);
.map(|dir| Arc::from(dir.join(name.0.as_ref())))
}
let task = cx.spawn({
let container_dir = container_dir.clone();
move |mut cx| async move {
let project_environment = project_environment.await;
let binary_result = adapter
.clone()
.get_language_server_command(container_dir, delegate.clone(), &mut cx)
.await;
delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
let mut binary = binary_result?;
// If we do have a project environment (either by spawning a shell in in the project directory
// or by getting it from the CLI) and the language server command itself
// doesn't have an environment (which it would have, if it was found in $PATH), then
// we use the project environment.
if binary.env.is_none() && project_environment.is_some() {
log::info!(
"using project environment for language server {:?}, id: {server_id}",
adapter.name.0
);
binary.env = project_environment.clone();
}
let options = adapter
.adapter
.clone()
.initialization_options(&delegate)
.await?;
#[cfg(any(test, feature = "test-support"))]
if true {
if let Some(this) = this.upgrade() {
if let Some(fake_entry) = this
.state
.write()
.fake_server_entries
.get_mut(&adapter.name)
{
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
server_id,
binary,
adapter.name.0.to_string(),
fake_entry.capabilities.clone(),
cx.clone(),
);
fake_entry._server = Some(fake_server.clone());
if let Some(initializer) = &fake_entry.initializer {
initializer(&mut fake_server);
}
let tx = fake_entry.tx.clone();
cx.background_executor()
.spawn(async move {
if fake_server
.try_receive_notification::<lsp::notification::Initialized>(
)
.await
.is_some()
{
tx.unbounded_send(fake_server.clone()).ok();
}
})
.detach();
return Ok((server, options));
}
}
}
drop(this);
Ok((
lsp::LanguageServer::new(
stderr_capture,
server_id,
binary,
&root_path,
adapter.code_action_kinds(),
cx,
)?,
options,
))
}
});
Some(PendingLanguageServer {
#[cfg(any(test, feature = "test-support"))]
pub fn create_fake_language_server(
&self,
server_id: LanguageServerId,
name: &LanguageServerName,
binary: lsp::LanguageServerBinary,
cx: gpui::AsyncAppContext,
) -> Option<lsp::LanguageServer> {
let mut state = self.state.write();
let fake_entry = state.fake_server_entries.get_mut(&name)?;
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
server_id,
task,
container_dir,
})
binary,
name.0.to_string(),
fake_entry.capabilities.clone(),
cx.clone(),
);
fake_entry._server = Some(fake_server.clone());
if let Some(initializer) = &fake_entry.initializer {
initializer(&mut fake_server);
}
let tx = fake_entry.tx.clone();
cx.background_executor()
.spawn(async move {
if fake_server
.try_receive_notification::<lsp::notification::Initialized>()
.await
.is_some()
{
tx.unbounded_send(fake_server.clone()).ok();
}
})
.detach();
Some(server)
}
pub fn language_server_binary_statuses(
@ -1007,29 +930,16 @@ impl LanguageRegistry {
self.lsp_binary_status_tx.subscribe()
}
pub fn delete_server_container(
&self,
adapter: Arc<CachedLspAdapter>,
cx: &mut AppContext,
) -> Task<()> {
pub async fn delete_server_container(&self, name: LanguageServerName) {
log::info!("deleting server container");
let Some(dir) = self.language_server_download_dir(&name) else {
return;
};
let download_dir = self
.language_server_download_dir
.clone()
.expect("language server download directory has not been assigned before deleting server container");
cx.spawn(|_| async move {
let container_dir = download_dir.join(adapter.name.0.as_ref());
smol::fs::remove_dir_all(container_dir)
.await
.context("server container removal")
.log_err();
})
}
pub fn next_language_server_id(&self) -> LanguageServerId {
self.state.write().next_language_server_id()
smol::fs::remove_dir_all(dir)
.await
.context("server container removal")
.log_err();
}
}