language: Move language server update to background when it takes too long (#43164)

Closes https://github.com/zed-industries/zed/issues/42360

If updating a language server takes longer than 10 seconds, we now fall
back to launching the currently installed version (if exists) and
continue downloading the update in the background.

Release Notes:

- Improved language server updates for slow connection, now Zed launches
existing server if the update is taking too long.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
Smit Barmase
2025-11-21 14:19:55 +05:30
committed by GitHub
co-authored by Piotr Osiewicz
parent bb514c158e
commit e2f6422b3e
7 changed files with 161 additions and 157 deletions
+3 -14
View File
@@ -1,13 +1,11 @@
use anyhow::{Context as _, Result};
use anyhow::Result;
use async_trait::async_trait;
use futures::StreamExt;
use gpui::AsyncApp;
use language::{LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy};
use project::lsp_store::language_server_settings;
use serde_json::json;
use smol::fs;
use std::{
ffi::OsString,
path::{Path, PathBuf},
@@ -176,19 +174,10 @@ async fn get_cached_server_binary(
node: &NodeRuntime,
) -> Option<LanguageServerBinary> {
maybe!(async {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_version_dir = Some(entry.path());
}
}
let last_version_dir = last_version_dir.context("no cached binary")?;
let server_path = last_version_dir.join(SERVER_PATH);
let server_path = container_dir.join(SERVER_PATH);
anyhow::ensure!(
server_path.exists(),
"missing executable in directory {last_version_dir:?}"
"missing executable in directory {server_path:?}"
);
Ok(LanguageServerBinary {
path: node.binary_path().await?,
+2 -12
View File
@@ -301,20 +301,10 @@ async fn get_cached_server_binary(
node: &NodeRuntime,
) -> Option<LanguageServerBinary> {
maybe!(async {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_version_dir = Some(entry.path());
}
}
let last_version_dir = last_version_dir.context("no cached binary")?;
let server_path = last_version_dir.join(SERVER_PATH);
let server_path = container_dir.join(SERVER_PATH);
anyhow::ensure!(
server_path.exists(),
"missing executable in directory {last_version_dir:?}"
"missing executable in directory {server_path:?}"
);
Ok(LanguageServerBinary {
path: node.binary_path().await?,
+3 -14
View File
@@ -1,14 +1,12 @@
use anyhow::{Context as _, Result};
use anyhow::Result;
use async_trait::async_trait;
use collections::HashMap;
use futures::StreamExt;
use gpui::AsyncApp;
use language::{LanguageName, LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy};
use project::lsp_store::language_server_settings;
use serde_json::{Value, json};
use smol::fs;
use std::{
ffi::OsString,
path::{Path, PathBuf},
@@ -198,19 +196,10 @@ async fn get_cached_server_binary(
node: &NodeRuntime,
) -> Option<LanguageServerBinary> {
maybe!(async {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_version_dir = Some(entry.path());
}
}
let last_version_dir = last_version_dir.context("no cached binary")?;
let server_path = last_version_dir.join(SERVER_PATH);
let server_path = container_dir.join(SERVER_PATH);
anyhow::ensure!(
server_path.exists(),
"missing executable in directory {last_version_dir:?}"
"missing executable in directory {server_path:?}"
);
Ok(LanguageServerBinary {
path: node.binary_path().await?,
+3 -14
View File
@@ -1,6 +1,5 @@
use anyhow::{Context as _, Result};
use anyhow::Result;
use async_trait::async_trait;
use futures::StreamExt;
use gpui::AsyncApp;
use language::{
LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain, language_settings::AllLanguageSettings,
@@ -10,7 +9,6 @@ use node_runtime::{NodeRuntime, VersionStrategy};
use project::lsp_store::language_server_settings;
use serde_json::Value;
use settings::{Settings, SettingsLocation};
use smol::fs;
use std::{
ffi::OsString,
path::{Path, PathBuf},
@@ -171,19 +169,10 @@ async fn get_cached_server_binary(
node: &NodeRuntime,
) -> Option<LanguageServerBinary> {
maybe!(async {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_version_dir = Some(entry.path());
}
}
let last_version_dir = last_version_dir.context("no cached binary")?;
let server_path = last_version_dir.join(SERVER_PATH);
let server_path = container_dir.join(SERVER_PATH);
anyhow::ensure!(
server_path.exists(),
"missing executable in directory {last_version_dir:?}"
"missing executable in directory {server_path:?}"
);
Ok(LanguageServerBinary {
path: node.binary_path().await?,