Compare commits
2 Commits
fix-git-ht
...
disable-au
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdd9e72557 | ||
|
|
b7dbf68c18 |
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -5298,6 +5298,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6846,6 +6847,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"async_zip",
|
||||
"futures 0.3.30",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"log",
|
||||
"paths",
|
||||
@@ -6856,6 +6858,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"util",
|
||||
"walkdir",
|
||||
"which 6.0.2",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -570,6 +570,12 @@
|
||||
// A list of globs representing files that inline completions should be disabled for.
|
||||
"disabled_globs": [".env"]
|
||||
},
|
||||
// Configuration for how Zed should handle missing binary dependencies (like language servers)
|
||||
// required by zed or the extensions you install.
|
||||
// By default "automatic" will use, install, and update dependencies on your behalf (if they are not already available)
|
||||
// "never" will silently ignore requests
|
||||
// If you choose never, you must maintain the required binaries yourself.
|
||||
"dependency_management": "automatic",
|
||||
// Settings specific to journaling
|
||||
"journal": {
|
||||
// The path of the directory where journal entries are stored
|
||||
|
||||
@@ -15,12 +15,11 @@ use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::ops::Range;
|
||||
use std::{
|
||||
any::Any,
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, AssetVersion, ResultExt};
|
||||
use wasmtime_wasi::WasiView as _;
|
||||
|
||||
pub struct ExtensionLspAdapter {
|
||||
@@ -38,7 +37,6 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Arc<Language>,
|
||||
_: Arc<Path>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
@@ -104,13 +102,13 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
unreachable!("get_language_server_command is overridden")
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: Box<dyn AssetVersion>,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
|
||||
@@ -12,6 +12,7 @@ use isahc::config::{Configurable, RedirectPolicy};
|
||||
use language::{
|
||||
language_settings::AllLanguageSettings, LanguageServerBinaryStatus, LspAdapterDelegate,
|
||||
};
|
||||
use node_runtime::NodeAssetVersion;
|
||||
use project::project_settings::ProjectSettings;
|
||||
use semantic_version::SemanticVersion;
|
||||
use std::{
|
||||
@@ -269,6 +270,7 @@ impl nodejs::Host for WasmState {
|
||||
.node_runtime
|
||||
.npm_package_latest_version(&package_name)
|
||||
.await
|
||||
.map(|version| version.version)
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
@@ -290,7 +292,13 @@ impl nodejs::Host for WasmState {
|
||||
) -> wasmtime::Result<Result<(), String>> {
|
||||
self.host
|
||||
.node_runtime
|
||||
.npm_install_packages(&self.work_dir(), &[(&package_name, &version)])
|
||||
.npm_install_packages(
|
||||
&self.work_dir(),
|
||||
&[NodeAssetVersion {
|
||||
name: package_name,
|
||||
version: version,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
@@ -10,3 +10,8 @@ This list should be updated as we notice things that should be changed so that w
|
||||
|
||||
- Rename `SlashCommand.tooltip_text` to `SlashCommand.menu_text`
|
||||
- We may even want to remove it entirely, as right now this is only used for featured slash commands, and slash commands defined by extensions aren't currently able to be featured.
|
||||
|
||||
### Expose extension binaries
|
||||
|
||||
- At a basic level, we want to be able to know what language servers an extension may install so that the user can approve them.
|
||||
- This may tie in to a more general extension capability system.
|
||||
|
||||
@@ -26,3 +26,4 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
futures-lite.workspace = true
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
@@ -10,6 +10,16 @@ pub struct GitHubLspBinaryVersion {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
impl util::AssetVersion for GitHubLspBinaryVersion {
|
||||
fn description(&self) -> String {
|
||||
format!("{} from {}", self.name, self.url)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GithubRelease {
|
||||
pub tag_name: String,
|
||||
|
||||
@@ -42,7 +42,6 @@ use serde_json::Value;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::num::NonZeroU32;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
@@ -61,7 +60,7 @@ use task::RunnableTag;
|
||||
pub use task_context::{ContextProvider, RunnableRange};
|
||||
use theme::SyntaxTheme;
|
||||
use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore};
|
||||
use util::serde::default_true;
|
||||
use util::{serde::default_true, AssetVersion};
|
||||
|
||||
pub use buffer::Operation;
|
||||
pub use buffer::*;
|
||||
@@ -177,9 +176,16 @@ impl CachedLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
self.adapter.check_if_user_installed(&*delegate, cx).await
|
||||
}
|
||||
|
||||
pub async fn get_language_server_command(
|
||||
self: Arc<Self>,
|
||||
language: Arc<Language>,
|
||||
container_dir: Arc<Path>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
@@ -187,7 +193,7 @@ impl CachedLspAdapter {
|
||||
let cached_binary = self.cached_binary.lock().await;
|
||||
self.adapter
|
||||
.clone()
|
||||
.get_language_server_command(language, container_dir, delegate, cached_binary, cx)
|
||||
.get_language_server_command(container_dir, delegate, cached_binary, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -278,34 +284,12 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
language: Arc<Language>,
|
||||
container_dir: Arc<Path>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
cx: &'a mut AsyncAppContext,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
async move {
|
||||
// First we check whether the adapter can give us a user-installed binary.
|
||||
// If so, we do *not* want to cache that, because each worktree might give us a different
|
||||
// binary:
|
||||
//
|
||||
// worktree 1: user-installed at `.bin/gopls`
|
||||
// worktree 2: user-installed at `~/bin/gopls`
|
||||
// worktree 3: no gopls found in PATH -> fallback to Zed installation
|
||||
//
|
||||
// We only want to cache when we fall back to the global one,
|
||||
// because we don't want to download and overwrite our global one
|
||||
// for each worktree we might have open.
|
||||
if let Some(binary) = self.check_if_user_installed(delegate.as_ref(), cx).await {
|
||||
log::info!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
language.name(),
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
return Ok(binary);
|
||||
}
|
||||
|
||||
if let Some(cached_binary) = cached_binary.as_ref() {
|
||||
return Ok(cached_binary.clone());
|
||||
}
|
||||
@@ -359,7 +343,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>>;
|
||||
) -> Result<Box<dyn AssetVersion>>;
|
||||
|
||||
fn will_fetch_server(
|
||||
&self,
|
||||
@@ -379,7 +363,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary>;
|
||||
@@ -1634,7 +1618,6 @@ impl LspAdapter for FakeLspAdapter {
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Arc<Language>,
|
||||
_: Arc<Path>,
|
||||
_: Arc<dyn LspAdapterDelegate>,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
@@ -1646,13 +1629,13 @@ impl LspAdapter for FakeLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: Box<dyn AssetVersion>,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
language_settings::{
|
||||
all_language_settings, AllLanguageSettingsContent, LanguageSettingsContent,
|
||||
all_language_settings, AllLanguageSettings, AllLanguageSettingsContent,
|
||||
DependencyManagement, LanguageSettingsContent,
|
||||
},
|
||||
task_context::ContextProvider,
|
||||
with_parser, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
|
||||
@@ -19,6 +20,7 @@ use gpui::{AppContext, BackgroundExecutor, Task};
|
||||
use lsp::LanguageServerId;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use postage::watch;
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsStr,
|
||||
@@ -745,6 +747,8 @@ impl LanguageRegistry {
|
||||
let login_shell_env_loaded = self.login_shell_env_loaded.clone();
|
||||
let this = Arc::downgrade(self);
|
||||
|
||||
let dependency_management = AllLanguageSettings::get_global(cx).dependency_management;
|
||||
|
||||
let task = cx.spawn({
|
||||
let container_dir = container_dir.clone();
|
||||
move |mut cx| async move {
|
||||
@@ -752,15 +756,36 @@ impl LanguageRegistry {
|
||||
// the login shell to be set on our process.
|
||||
login_shell_env_loaded.await;
|
||||
|
||||
let binary_result = adapter
|
||||
let binary_result = if let Some(binary) = adapter
|
||||
.clone()
|
||||
.get_language_server_command(
|
||||
language.clone(),
|
||||
container_dir,
|
||||
delegate.clone(),
|
||||
&mut cx,
|
||||
)
|
||||
.await;
|
||||
.check_if_user_installed(delegate.clone(), &mut cx)
|
||||
.await
|
||||
{
|
||||
log::info!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
language.name(),
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
Ok(binary)
|
||||
} else {
|
||||
match dependency_management {
|
||||
DependencyManagement::Automatic => {
|
||||
adapter
|
||||
.clone()
|
||||
.get_language_server_command(
|
||||
container_dir,
|
||||
delegate.clone(),
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
}
|
||||
DependencyManagement::None => Err(anyhow!(
|
||||
"No language server found for {}. Not installing because \"dependency_management\" is set to \"never\".",
|
||||
language.name()
|
||||
)),
|
||||
}
|
||||
};
|
||||
|
||||
delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
|
||||
|
||||
|
||||
@@ -53,6 +53,14 @@ pub fn all_language_settings<'a>(
|
||||
AllLanguageSettings::get(location, cx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DependencyManagement {
|
||||
#[default]
|
||||
Automatic, // Zed manages everything (how it works today)
|
||||
None, // We don't do anything except error
|
||||
}
|
||||
|
||||
/// The settings for all languages.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AllLanguageSettings {
|
||||
@@ -60,6 +68,7 @@ pub struct AllLanguageSettings {
|
||||
pub inline_completions: InlineCompletionSettings,
|
||||
defaults: LanguageSettings,
|
||||
languages: HashMap<Arc<str>, LanguageSettings>,
|
||||
pub dependency_management: DependencyManagement,
|
||||
pub(crate) file_types: HashMap<Arc<str>, GlobSet>,
|
||||
}
|
||||
|
||||
@@ -205,6 +214,9 @@ pub struct AllLanguageSettingsContent {
|
||||
/// The inline completion settings.
|
||||
#[serde(default)]
|
||||
pub inline_completions: Option<InlineCompletionSettingsContent>,
|
||||
/// How zed should manage external dependencies
|
||||
#[serde(default)]
|
||||
pub dependency_management: Option<DependencyManagement>,
|
||||
/// The default language settings.
|
||||
#[serde(flatten)]
|
||||
pub defaults: LanguageSettingsContent,
|
||||
@@ -876,6 +888,12 @@ impl settings::Settings for AllLanguageSettings {
|
||||
}
|
||||
|
||||
let mut copilot_enabled = default_value.features.as_ref().and_then(|f| f.copilot);
|
||||
let dependency_management = sources
|
||||
.user
|
||||
.and_then(|user| user.dependency_management)
|
||||
.or(default_value.dependency_management)
|
||||
.unwrap();
|
||||
|
||||
let mut inline_completion_provider = default_value
|
||||
.features
|
||||
.as_ref()
|
||||
@@ -968,6 +986,7 @@ impl settings::Settings for AllLanguageSettings {
|
||||
.filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
|
||||
.collect(),
|
||||
},
|
||||
dependency_management,
|
||||
defaults,
|
||||
languages,
|
||||
file_types,
|
||||
|
||||
@@ -8,8 +8,8 @@ use lsp::LanguageServerBinary;
|
||||
use project::project_settings::{BinarySettings, ProjectSettings};
|
||||
use settings::Settings;
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
use std::{env::consts, path::PathBuf, sync::Arc};
|
||||
use util::{fs::remove_matching, maybe, AssetVersion, ResultExt};
|
||||
|
||||
pub struct CLspAdapter;
|
||||
|
||||
@@ -68,7 +68,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
let release =
|
||||
latest_github_release("clangd/clangd", true, false, delegate.http_client()).await?;
|
||||
let os_suffix = match consts::OS {
|
||||
@@ -92,11 +92,14 @@ impl super::LspAdapter for CLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let version = version
|
||||
.as_any()
|
||||
.downcast_ref::<GitHubLspBinaryVersion>()
|
||||
.unwrap();
|
||||
let zip_path = container_dir.join(format!("clangd_{}.zip", version.name));
|
||||
let version_dir = container_dir.join(format!("clangd_{}", version.name));
|
||||
let binary_path = version_dir.join("bin/clangd");
|
||||
|
||||
@@ -3,16 +3,15 @@ use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use node_runtime::{NodeAssetVersion, NodeRuntime};
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, AssetVersion, ResultExt};
|
||||
|
||||
const SERVER_PATH: &str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-css-language-server";
|
||||
@@ -40,7 +39,7 @@ impl LspAdapter for CssLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
@@ -50,22 +49,25 @@ impl LspAdapter for CssLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let latest_version = latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<NodeAssetVersion>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let package_name = "vscode-langservers-extracted";
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(package_name, &server_path, &container_dir, &latest_version)
|
||||
.should_install_npm_package(&latest_version, &server_path, &container_dir)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
self.node
|
||||
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
|
||||
.npm_install_packages(&container_dir, &[latest_version.clone()])
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use std::{
|
||||
},
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
use util::{fs::remove_matching, maybe, AssetVersion, ResultExt};
|
||||
|
||||
fn server_binary_arguments() -> Vec<OsString> {
|
||||
vec!["-mode=stdio".into()]
|
||||
@@ -44,6 +44,20 @@ lazy_static! {
|
||||
static ref GO_ESCAPE_SUBTEST_NAME_REGEX: Regex = Regex::new(r#"[.*+?^${}()|\[\]\\]"#).unwrap();
|
||||
}
|
||||
|
||||
struct GoLspAssetVersion {
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
impl util::AssetVersion for GoLspAssetVersion {
|
||||
fn description(&self) -> String {
|
||||
format!("gopls version {:?}", self.version)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl super::LspAdapter for GoLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
@@ -53,7 +67,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
let release =
|
||||
latest_github_release("golang/tools", false, false, delegate.http_client()).await?;
|
||||
let version: Option<String> = release.tag_name.strip_prefix("gopls/v").map(str::to_string);
|
||||
@@ -63,7 +77,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
release.tag_name
|
||||
);
|
||||
}
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
Ok(Box::new(GoLspAssetVersion { version }) as Box<_>)
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -138,14 +152,18 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<Option<String>>().unwrap();
|
||||
let version = &version
|
||||
.as_any()
|
||||
.downcast_ref::<GoLspAssetVersion>()
|
||||
.unwrap()
|
||||
.version;
|
||||
let this = *self;
|
||||
|
||||
if let Some(version) = *version {
|
||||
if let Some(version) = version {
|
||||
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||
if let Ok(metadata) = fs::metadata(&binary_path).await {
|
||||
if metadata.is_file() {
|
||||
|
||||
@@ -9,7 +9,7 @@ use gpui::{AppContext, AsyncAppContext};
|
||||
use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use node_runtime::{NodeAssetVersion, NodeRuntime};
|
||||
use project::ContextProviderWithTasks;
|
||||
use serde_json::{json, Value};
|
||||
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
|
||||
@@ -18,7 +18,6 @@ use smol::{
|
||||
io::BufReader,
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
env::consts,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
@@ -26,7 +25,7 @@ use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, VariableName};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
use util::{fs::remove_matching, maybe, AssetVersion, ResultExt};
|
||||
|
||||
const SERVER_PATH: &str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-json-language-server";
|
||||
@@ -142,7 +141,7 @@ impl LspAdapter for JsonLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
@@ -152,22 +151,25 @@ impl LspAdapter for JsonLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let latest_version = latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<NodeAssetVersion>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let package_name = "vscode-langservers-extracted";
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(package_name, &server_path, &container_dir, &latest_version)
|
||||
.should_install_npm_package(&latest_version, &server_path, &container_dir)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
self.node
|
||||
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
|
||||
.npm_install_packages(&container_dir, &[latest_version])
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -277,7 +279,7 @@ impl LspAdapter for NodeVersionAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
let release = latest_github_release(
|
||||
"zed-industries/package-version-server",
|
||||
true,
|
||||
@@ -310,11 +312,14 @@ impl LspAdapter for NodeVersionAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let version = latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<GitHubLspBinaryVersion>()
|
||||
.unwrap();
|
||||
let destination_path = container_dir.join(format!(
|
||||
"package-version-server-{}{}",
|
||||
version.name,
|
||||
|
||||
@@ -2,14 +2,14 @@ use anyhow::Context;
|
||||
use gpui::{AppContext, UpdateGlobal};
|
||||
use json::json_task_context;
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use node_runtime::{NodeAssetVersion, NodeRuntime};
|
||||
use python::PythonContextProvider;
|
||||
use rust_embed::RustEmbed;
|
||||
use settings::SettingsStore;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{str, sync::Arc};
|
||||
use std::{any::Any, str, sync::Arc};
|
||||
use typescript::typescript_task_context;
|
||||
use util::{asset_str, ResultExt};
|
||||
use util::{asset_str, AssetVersion, ResultExt};
|
||||
|
||||
use crate::{bash::bash_task_context, go::GoContextProvider, rust::RustContextProvider};
|
||||
|
||||
@@ -311,3 +311,22 @@ fn load_queries(name: &str) -> LanguageQueries {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
struct TypeScriptVersions {
|
||||
typescript_version: NodeAssetVersion,
|
||||
server_version: NodeAssetVersion,
|
||||
}
|
||||
|
||||
impl AssetVersion for TypeScriptVersions {
|
||||
fn description(&self) -> String {
|
||||
format!(
|
||||
"{} (LSP: {})",
|
||||
self.typescript_version.description(),
|
||||
self.server_version.description()
|
||||
)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,19 @@ use gpui::AppContext;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeAssetVersion;
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::project_settings::ProjectSettings;
|
||||
use serde_json::Value;
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, VariableName};
|
||||
use util::AssetVersion;
|
||||
use util::ResultExt;
|
||||
|
||||
const SERVER_PATH: &str = "node_modules/pyright/langserver.index.js";
|
||||
@@ -45,7 +46,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version(Self::SERVER_NAME)
|
||||
@@ -55,22 +56,31 @@ impl LspAdapter for PythonLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let latest_version = &latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<NodeAssetVersion>()
|
||||
.unwrap()
|
||||
.version;
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let package_name = Self::SERVER_NAME;
|
||||
|
||||
let node_asset = NodeAssetVersion {
|
||||
name: package_name.to_string(),
|
||||
version: latest_version.to_string(),
|
||||
};
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(package_name, &server_path, &container_dir, &latest_version)
|
||||
.should_install_npm_package(&node_asset, &server_path, &container_dir)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
self.node
|
||||
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
|
||||
.npm_install_packages(&container_dir, &[node_asset])
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,14 +13,13 @@ use regex::Regex;
|
||||
use settings::Settings;
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
env::consts,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
use util::{fs::remove_matching, maybe, AssetVersion, ResultExt};
|
||||
|
||||
pub struct RustLspAdapter;
|
||||
|
||||
@@ -85,7 +84,7 @@ impl LspAdapter for RustLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
let release = latest_github_release(
|
||||
"rust-lang/rust-analyzer",
|
||||
true,
|
||||
@@ -113,11 +112,14 @@ impl LspAdapter for RustLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let version = &version
|
||||
.as_any()
|
||||
.downcast_ref::<GitHubLspBinaryVersion>()
|
||||
.unwrap();
|
||||
let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name));
|
||||
|
||||
if fs::metadata(&destination_path).await.is_err() {
|
||||
|
||||
@@ -5,18 +5,17 @@ use futures::StreamExt;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use node_runtime::{NodeAssetVersion, NodeRuntime};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use serde_json::{json, Value};
|
||||
use settings::Settings;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, AssetVersion, ResultExt};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const SERVER_PATH: &str = "node_modules/.bin/tailwindcss-language-server.ps1";
|
||||
@@ -82,7 +81,7 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("@tailwindcss/language-server")
|
||||
@@ -92,22 +91,25 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let latest_version = latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<NodeAssetVersion>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let package_name = "@tailwindcss/language-server";
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(package_name, &server_path, &container_dir, &latest_version)
|
||||
.should_install_npm_package(&latest_version, &server_path, &container_dir)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
self.node
|
||||
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
|
||||
.npm_install_packages(&container_dir, &[latest_version])
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,14 @@ use serde_json::{json, Value};
|
||||
use settings::Settings;
|
||||
use smol::{fs, io::BufReader, stream::StreamExt};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, VariableName};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
use util::{fs::remove_matching, maybe, AssetVersion, ResultExt};
|
||||
|
||||
use crate::TypeScriptVersions;
|
||||
|
||||
pub(super) fn typescript_task_context() -> ContextProviderWithTasks {
|
||||
ContextProviderWithTasks::new(TaskTemplates(vec![
|
||||
@@ -86,11 +87,6 @@ impl TypeScriptLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeScriptVersions {
|
||||
typescript_version: String,
|
||||
server_version: String,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for TypeScriptLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
@@ -100,7 +96,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(TypeScriptVersions {
|
||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||
server_version: self
|
||||
@@ -112,21 +108,22 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
||||
let latest_version = &latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<TypeScriptVersions>()
|
||||
.unwrap();
|
||||
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
||||
let package_name = "typescript";
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(
|
||||
package_name,
|
||||
&latest_version.typescript_version,
|
||||
&server_path,
|
||||
&container_dir,
|
||||
latest_version.typescript_version.as_str(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -135,11 +132,8 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[
|
||||
(package_name, latest_version.typescript_version.as_str()),
|
||||
(
|
||||
"typescript-language-server",
|
||||
latest_version.server_version.as_str(),
|
||||
),
|
||||
latest_version.typescript_version.clone(),
|
||||
latest_version.server_version.clone(),
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
@@ -410,7 +404,7 @@ impl LspAdapter for EsLintLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
let url = build_asset_url(
|
||||
"microsoft/vscode-eslint",
|
||||
Self::CURRENT_VERSION,
|
||||
@@ -425,11 +419,14 @@ impl LspAdapter for EsLintLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let version = &version
|
||||
.as_any()
|
||||
.downcast_ref::<GitHubLspBinaryVersion>()
|
||||
.unwrap();
|
||||
let destination_path = container_dir.join(format!("vscode-eslint-{}", version.name));
|
||||
let server_path = destination_path.join(Self::SERVER_PATH);
|
||||
|
||||
|
||||
@@ -9,12 +9,13 @@ use project::project_settings::{BinarySettings, ProjectSettings};
|
||||
use serde_json::{json, Value};
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, AssetVersion, ResultExt};
|
||||
|
||||
use crate::TypeScriptVersions;
|
||||
|
||||
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
@@ -44,11 +45,6 @@ impl VtslsLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeScriptVersions {
|
||||
typescript_version: String,
|
||||
server_version: String,
|
||||
}
|
||||
|
||||
const SERVER_NAME: &'static str = "vtsls";
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for VtslsLspAdapter {
|
||||
@@ -59,7 +55,7 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(TypeScriptVersions {
|
||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||
server_version: self
|
||||
@@ -113,21 +109,22 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
||||
let latest_version = &latest_version
|
||||
.as_any()
|
||||
.downcast_ref::<TypeScriptVersions>()
|
||||
.unwrap();
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
let package_name = "typescript";
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(
|
||||
package_name,
|
||||
&latest_version.typescript_version,
|
||||
&server_path,
|
||||
&container_dir,
|
||||
latest_version.typescript_version.as_str(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -136,11 +133,8 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[
|
||||
(package_name, latest_version.typescript_version.as_str()),
|
||||
(
|
||||
"@vtsls/language-server",
|
||||
latest_version.server_version.as_str(),
|
||||
),
|
||||
latest_version.typescript_version.clone(),
|
||||
latest_version.server_version.clone(),
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -6,16 +6,15 @@ use language::{
|
||||
language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use node_runtime::{NodeAssetVersion, NodeRuntime};
|
||||
use serde_json::Value;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, AssetVersion, ResultExt};
|
||||
|
||||
const SERVER_PATH: &str = "node_modules/yaml-language-server/bin/yaml-language-server";
|
||||
|
||||
@@ -42,7 +41,7 @@ impl LspAdapter for YamlLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
) -> Result<Box<dyn AssetVersion>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("yaml-language-server")
|
||||
@@ -52,22 +51,27 @@ impl LspAdapter for YamlLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Box<dyn AssetVersion>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let latest_version = &latest_version.as_any().downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let package_name = "yaml-language-server";
|
||||
|
||||
let node_asset = NodeAssetVersion {
|
||||
name: package_name.to_string(),
|
||||
version: latest_version.to_string(),
|
||||
};
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(package_name, &server_path, &container_dir, &latest_version)
|
||||
.should_install_npm_package(&node_asset, &server_path, &container_dir)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
self.node
|
||||
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
|
||||
.npm_install_packages(&container_dir, &[node_asset])
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ smol.workspace = true
|
||||
tempfile = { workspace = true, optional = true }
|
||||
util.workspace = true
|
||||
walkdir = "2.5.0"
|
||||
gpui.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
async-std = { version = "1.12.0", features = ["unstable"] }
|
||||
|
||||
@@ -4,25 +4,32 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
pub use archive::extract_zip;
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use futures::channel::mpsc;
|
||||
use futures::channel::oneshot;
|
||||
use futures::AsyncReadExt;
|
||||
use gpui::SemanticVersion;
|
||||
use http_client::HttpClient;
|
||||
use semver::Version;
|
||||
use serde::Deserialize;
|
||||
use smol::io::BufReader;
|
||||
use smol::{fs, lock::Mutex, process::Command};
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use std::process::{Output, Stdio};
|
||||
use std::str::from_utf8;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
env::consts,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use util::{AssetVersion, ResultExt};
|
||||
|
||||
#[cfg(windows)]
|
||||
use smol::process::windows::CommandExt;
|
||||
|
||||
const VERSION: &str = "v22.5.1";
|
||||
const VERSION: SemanticVersion = SemanticVersion::new(22, 5, 1);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const NODE_PATH: &str = "bin/node";
|
||||
@@ -52,6 +59,28 @@ pub struct NpmInfoDistTags {
|
||||
latest: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeAssetVersion {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Display for NodeAssetVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}@{}", self.name, self.version))
|
||||
}
|
||||
}
|
||||
|
||||
impl util::AssetVersion for NodeAssetVersion {
|
||||
fn description(&self) -> String {
|
||||
format!("node module {}@{}", self.name, self.version)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait NodeRuntime: Send + Sync {
|
||||
async fn binary_path(&self) -> Result<PathBuf>;
|
||||
@@ -63,10 +92,13 @@ pub trait NodeRuntime: Send + Sync {
|
||||
args: &[&str],
|
||||
) -> Result<Output>;
|
||||
|
||||
async fn npm_package_latest_version(&self, name: &str) -> Result<String>;
|
||||
async fn npm_package_latest_version(&self, name: &str) -> Result<NodeAssetVersion>;
|
||||
|
||||
async fn npm_install_packages(&self, directory: &Path, packages: &[(&str, &str)])
|
||||
-> Result<()>;
|
||||
async fn npm_install_packages(
|
||||
&self,
|
||||
directory: &Path,
|
||||
packages: &[NodeAssetVersion],
|
||||
) -> Result<()>;
|
||||
|
||||
async fn npm_package_installed_version(
|
||||
&self,
|
||||
@@ -76,10 +108,9 @@ pub trait NodeRuntime: Send + Sync {
|
||||
|
||||
async fn should_install_npm_package(
|
||||
&self,
|
||||
package_name: &str,
|
||||
package: &NodeAssetVersion,
|
||||
local_executable_path: &Path,
|
||||
local_package_directory: &PathBuf,
|
||||
latest_version: &str,
|
||||
) -> bool {
|
||||
// In the case of the local system not having the package installed,
|
||||
// or in the instances where we fail to parse package.json data,
|
||||
@@ -89,7 +120,7 @@ pub trait NodeRuntime: Send + Sync {
|
||||
}
|
||||
|
||||
let Some(installed_version) = self
|
||||
.npm_package_installed_version(local_package_directory, package_name)
|
||||
.npm_package_installed_version(local_package_directory, &package.name)
|
||||
.await
|
||||
.log_err()
|
||||
.flatten()
|
||||
@@ -100,7 +131,7 @@ pub trait NodeRuntime: Send + Sync {
|
||||
let Some(installed_version) = Version::parse(&installed_version).log_err() else {
|
||||
return true;
|
||||
};
|
||||
let Some(latest_version) = Version::parse(&latest_version).log_err() else {
|
||||
let Some(latest_version) = Version::parse(&package.version).log_err() else {
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -111,19 +142,45 @@ pub trait NodeRuntime: Send + Sync {
|
||||
pub struct RealNodeRuntime {
|
||||
http: Arc<dyn HttpClient>,
|
||||
installation_lock: Mutex<()>,
|
||||
check_can_install: mpsc::UnboundedSender<(Box<dyn AssetVersion>, oneshot::Sender<Result<()>>)>,
|
||||
|
||||
can_use_dependencies: mpsc::UnboundedSender<oneshot::Sender<bool>>,
|
||||
}
|
||||
|
||||
impl RealNodeRuntime {
|
||||
pub fn new(http: Arc<dyn HttpClient>) -> Arc<dyn NodeRuntime> {
|
||||
pub fn new(
|
||||
http: Arc<dyn HttpClient>,
|
||||
check_can_install: mpsc::UnboundedSender<(
|
||||
Box<dyn AssetVersion>,
|
||||
oneshot::Sender<Result<()>>,
|
||||
)>,
|
||||
can_use_dependencies: mpsc::UnboundedSender<oneshot::Sender<bool>>,
|
||||
) -> Arc<dyn NodeRuntime> {
|
||||
Arc::new(RealNodeRuntime {
|
||||
http,
|
||||
installation_lock: Mutex::new(()),
|
||||
check_can_install,
|
||||
can_use_dependencies,
|
||||
})
|
||||
}
|
||||
|
||||
async fn install_if_needed(&self) -> Result<PathBuf> {
|
||||
let _lock = self.installation_lock.lock().await;
|
||||
async fn check_dependency(&self, asset_version: impl AssetVersion) -> Result<()> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.check_can_install
|
||||
.unbounded_send((Box::new(asset_version), tx))
|
||||
.ok();
|
||||
rx.await?
|
||||
}
|
||||
|
||||
async fn can_install_dependencies(&self) -> bool {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.can_use_dependencies.unbounded_send(tx).ok();
|
||||
rx.await.unwrap_or(false)
|
||||
}
|
||||
|
||||
async fn install_if_needed(&self) -> Result<(PathBuf, PathBuf, PathBuf)> {
|
||||
log::info!("Node runtime install_if_needed");
|
||||
let _lock = self.installation_lock.lock().await;
|
||||
|
||||
let os = match consts::OS {
|
||||
"macos" => "darwin",
|
||||
@@ -138,24 +195,101 @@ impl RealNodeRuntime {
|
||||
other => bail!("Running on unsupported architecture: {other}"),
|
||||
};
|
||||
|
||||
let folder_name = format!("node-{VERSION}-{os}-{arch}");
|
||||
let mut found_node_version: Option<SemanticVersion> = None;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let node_path_binary = "node";
|
||||
#[cfg(not(windows))]
|
||||
let npm_path_binary = "npm";
|
||||
|
||||
#[cfg(windows)]
|
||||
let node_path_binary = "node.exe";
|
||||
#[cfg(windows)]
|
||||
let npm_path_binary = "npm.exe"; // ???
|
||||
|
||||
if let Some(node_path) = which::which(node_path_binary).log_err() {
|
||||
if let Some(npm_path) = which::which(npm_path_binary).log_err() {
|
||||
let mut path_command = Command::new(dbg!(&node_path));
|
||||
path_command
|
||||
.env_clear()
|
||||
.arg("--version")
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::null());
|
||||
|
||||
#[cfg(windows)]
|
||||
path_command.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
|
||||
|
||||
let output = path_command.output().await;
|
||||
|
||||
if let Some(output) = output.log_err() {
|
||||
if output.status.success() {
|
||||
if let Ok(output) = from_utf8(&output.stdout) {
|
||||
let output = output.trim();
|
||||
let output = if let Some(output) = output.strip_prefix("v") {
|
||||
output
|
||||
} else {
|
||||
output
|
||||
};
|
||||
|
||||
if let Some(node_version) = SemanticVersion::from_str(output).log_err()
|
||||
{
|
||||
found_node_version = Some(node_version);
|
||||
if node_version >= VERSION {
|
||||
let folder_name = format!("node-v{node_version}-{os}-{arch}");
|
||||
let node_containing_dir = paths::support_dir().join("node");
|
||||
let node_dir = node_containing_dir.join(folder_name);
|
||||
|
||||
// Make sure the proper file structure is setup
|
||||
if fs::metadata(&node_dir)
|
||||
.await
|
||||
.is_err_and(|e| e.kind() == ErrorKind::NotFound)
|
||||
{
|
||||
_ = fs::create_dir_all(node_dir.join("cache")).await;
|
||||
_ = fs::write(node_dir.join("blank_user_npmrc"), []).await;
|
||||
_ = fs::write(node_dir.join("blank_global_npmrc"), [])
|
||||
.await;
|
||||
}
|
||||
|
||||
return Ok((node_path, npm_path, node_dir));
|
||||
} else {
|
||||
log::error!(
|
||||
"node version on PATH is too old: v{}, Zed requires: v{}",
|
||||
node_version,
|
||||
VERSION
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.can_install_dependencies().await {
|
||||
let err = if let Some(node_version) = found_node_version {
|
||||
anyhow!(
|
||||
"Node version on $PATH is too old. Verion required: {}, version found: {}",
|
||||
VERSION,
|
||||
node_version
|
||||
)
|
||||
} else {
|
||||
anyhow!("Could not find or use node on $PATH")
|
||||
};
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let folder_name = format!("node-v{VERSION}-{os}-{arch}");
|
||||
let node_containing_dir = paths::support_dir().join("node");
|
||||
let node_dir = node_containing_dir.join(folder_name);
|
||||
let node_binary = node_dir.join(NODE_PATH);
|
||||
let npm_file = node_dir.join(NPM_PATH);
|
||||
|
||||
let mut command = Command::new(&node_binary);
|
||||
|
||||
command
|
||||
.env_clear()
|
||||
.arg(npm_file)
|
||||
.arg("--version")
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args(["--cache".into(), node_dir.join("cache")])
|
||||
.args(["--userconfig".into(), node_dir.join("blank_user_npmrc")])
|
||||
.args(["--globalconfig".into(), node_dir.join("blank_global_npmrc")]);
|
||||
.stdout(Stdio::null());
|
||||
|
||||
#[cfg(windows)]
|
||||
command.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
|
||||
@@ -164,6 +298,12 @@ impl RealNodeRuntime {
|
||||
let valid = matches!(result, Ok(status) if status.success());
|
||||
|
||||
if !valid {
|
||||
self.check_dependency(NodeAssetVersion {
|
||||
name: "node".to_string(),
|
||||
version: format!("{}", VERSION),
|
||||
})
|
||||
.await?;
|
||||
|
||||
_ = fs::remove_dir_all(&node_containing_dir).await;
|
||||
fs::create_dir(&node_containing_dir)
|
||||
.await
|
||||
@@ -176,13 +316,13 @@ impl RealNodeRuntime {
|
||||
};
|
||||
|
||||
let file_name = format!(
|
||||
"node-{VERSION}-{os}-{arch}.{extension}",
|
||||
"node-v{VERSION}-{os}-{arch}.{extension}",
|
||||
extension = match archive_type {
|
||||
ArchiveType::TarGz => "tar.gz",
|
||||
ArchiveType::Zip => "zip",
|
||||
}
|
||||
);
|
||||
let url = format!("https://nodejs.org/dist/{VERSION}/{file_name}");
|
||||
let url = format!("https://nodejs.org/dist/v{VERSION}/{file_name}");
|
||||
let mut response = self
|
||||
.http
|
||||
.get(&url, Default::default(), true)
|
||||
@@ -198,22 +338,21 @@ impl RealNodeRuntime {
|
||||
}
|
||||
ArchiveType::Zip => archive::extract_zip(&node_containing_dir, body).await?,
|
||||
}
|
||||
|
||||
_ = fs::create_dir(node_dir.join("cache")).await;
|
||||
_ = fs::write(node_dir.join("blank_user_npmrc"), []).await;
|
||||
_ = fs::write(node_dir.join("blank_global_npmrc"), []).await;
|
||||
}
|
||||
|
||||
// Note: Not in the `if !valid {}` so we can populate these for existing installations
|
||||
_ = fs::create_dir(node_dir.join("cache")).await;
|
||||
_ = fs::write(node_dir.join("blank_user_npmrc"), []).await;
|
||||
_ = fs::write(node_dir.join("blank_global_npmrc"), []).await;
|
||||
|
||||
anyhow::Ok(node_dir)
|
||||
anyhow::Ok((node_dir.join(NODE_PATH), node_dir.join(NPM_PATH), node_dir))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl NodeRuntime for RealNodeRuntime {
|
||||
async fn binary_path(&self) -> Result<PathBuf> {
|
||||
let installation_path = self.install_if_needed().await?;
|
||||
Ok(installation_path.join(NODE_PATH))
|
||||
let (binary_path, _, _) = self.install_if_needed().await?;
|
||||
Ok(binary_path)
|
||||
}
|
||||
|
||||
async fn run_npm_subcommand(
|
||||
@@ -223,10 +362,8 @@ impl NodeRuntime for RealNodeRuntime {
|
||||
args: &[&str],
|
||||
) -> Result<Output> {
|
||||
let attempt = || async move {
|
||||
let installation_path = self.install_if_needed().await?;
|
||||
let (node_binary, npm_path, node_dir) = self.install_if_needed().await?;
|
||||
|
||||
let node_binary = installation_path.join(NODE_PATH);
|
||||
let npm_file = installation_path.join(NPM_PATH);
|
||||
let mut env_path = vec![node_binary
|
||||
.parent()
|
||||
.expect("invalid node binary path")
|
||||
@@ -244,23 +381,17 @@ impl NodeRuntime for RealNodeRuntime {
|
||||
return Err(anyhow!("missing node binary file"));
|
||||
}
|
||||
|
||||
if smol::fs::metadata(&npm_file).await.is_err() {
|
||||
if smol::fs::metadata(&npm_path).await.is_err() {
|
||||
return Err(anyhow!("missing npm file"));
|
||||
}
|
||||
|
||||
let mut command = Command::new(node_binary);
|
||||
command.env_clear();
|
||||
command.env("PATH", env_path);
|
||||
command.arg(npm_file).arg(subcommand);
|
||||
command.args(["--cache".into(), installation_path.join("cache")]);
|
||||
command.args([
|
||||
"--userconfig".into(),
|
||||
installation_path.join("blank_user_npmrc"),
|
||||
]);
|
||||
command.args([
|
||||
"--globalconfig".into(),
|
||||
installation_path.join("blank_global_npmrc"),
|
||||
]);
|
||||
command.arg(&npm_path).arg(subcommand);
|
||||
command.args(["--cache".into(), node_dir.join("cache")]);
|
||||
command.args(["--userconfig".into(), node_dir.join("blank_user_npmrc")]);
|
||||
command.args(["--globalconfig".into(), node_dir.join("blank_global_npmrc")]);
|
||||
command.args(args);
|
||||
|
||||
if let Some(directory) = directory {
|
||||
@@ -327,7 +458,7 @@ impl NodeRuntime for RealNodeRuntime {
|
||||
output.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
|
||||
async fn npm_package_latest_version(&self, name: &str) -> Result<String> {
|
||||
async fn npm_package_latest_version(&self, name: &str) -> Result<NodeAssetVersion> {
|
||||
let output = self
|
||||
.run_npm_subcommand(
|
||||
None,
|
||||
@@ -346,10 +477,16 @@ impl NodeRuntime for RealNodeRuntime {
|
||||
.await?;
|
||||
|
||||
let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
|
||||
info.dist_tags
|
||||
let version = info
|
||||
.dist_tags
|
||||
.latest
|
||||
.or_else(|| info.versions.pop())
|
||||
.ok_or_else(|| anyhow!("no version found for npm package {}", name))
|
||||
.ok_or_else(|| anyhow!("no version found for npm package {}", name))?;
|
||||
|
||||
Ok(NodeAssetVersion {
|
||||
name: name.to_string(),
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
async fn npm_package_installed_version(
|
||||
@@ -385,11 +522,19 @@ impl NodeRuntime for RealNodeRuntime {
|
||||
async fn npm_install_packages(
|
||||
&self,
|
||||
directory: &Path,
|
||||
packages: &[(&str, &str)],
|
||||
packages: &[NodeAssetVersion],
|
||||
) -> Result<()> {
|
||||
if packages.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.check_dependency(packages[0].clone()).await?;
|
||||
|
||||
let packages: Vec<_> = packages
|
||||
.into_iter()
|
||||
.map(|(name, version)| format!("{name}@{version}"))
|
||||
.map(|node_asset_version| {
|
||||
format!("{}@{}", node_asset_version.name, node_asset_version.version)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut arguments: Vec<_> = packages.iter().map(|p| p.as_str()).collect();
|
||||
@@ -432,7 +577,7 @@ impl NodeRuntime for FakeNodeRuntime {
|
||||
unreachable!("Should not run npm subcommand '{subcommand}' with args {args:?}")
|
||||
}
|
||||
|
||||
async fn npm_package_latest_version(&self, name: &str) -> anyhow::Result<String> {
|
||||
async fn npm_package_latest_version(&self, name: &str) -> anyhow::Result<NodeAssetVersion> {
|
||||
unreachable!("Should not query npm package '{name}' for latest version")
|
||||
}
|
||||
|
||||
@@ -447,7 +592,7 @@ impl NodeRuntime for FakeNodeRuntime {
|
||||
async fn npm_install_packages(
|
||||
&self,
|
||||
_: &Path,
|
||||
packages: &[(&str, &str)],
|
||||
packages: &[NodeAssetVersion],
|
||||
) -> anyhow::Result<()> {
|
||||
unreachable!("Should not install packages {packages:?}")
|
||||
}
|
||||
|
||||
@@ -13,13 +13,16 @@ use futures::{
|
||||
};
|
||||
use gpui::{AsyncAppContext, Model, ModelContext, Task, WeakModel};
|
||||
use language::{
|
||||
language_settings::{Formatter, LanguageSettings, SelectedFormatter},
|
||||
language_settings::{
|
||||
AllLanguageSettings, DependencyManagement, Formatter, LanguageSettings, SelectedFormatter,
|
||||
},
|
||||
Buffer, LanguageServerName, LocalFile,
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use node_runtime::NodeRuntime;
|
||||
use paths::default_prettier_dir;
|
||||
use prettier::Prettier;
|
||||
use settings::Settings;
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
|
||||
use crate::{
|
||||
@@ -372,13 +375,13 @@ async fn install_prettier_packages(
|
||||
.chain(Some(&"prettier".into()))
|
||||
.map(|package_name| async {
|
||||
let returned_package_name = package_name.to_string();
|
||||
let latest_version = node
|
||||
let asset_version = node
|
||||
.npm_package_latest_version(package_name)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("fetching latest npm version for package {returned_package_name}")
|
||||
})?;
|
||||
anyhow::Ok((returned_package_name, latest_version))
|
||||
anyhow::Ok(asset_version)
|
||||
}),
|
||||
)
|
||||
.await
|
||||
@@ -399,11 +402,7 @@ async fn install_prettier_packages(
|
||||
}
|
||||
|
||||
log::info!("Installing default prettier and plugins: {packages_to_versions:?}");
|
||||
let borrowed_packages = packages_to_versions
|
||||
.iter()
|
||||
.map(|(package, version)| (package.as_str(), version.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
node.npm_install_packages(default_prettier_dir, &borrowed_packages)
|
||||
node.npm_install_packages(default_prettier_dir, &packages_to_versions)
|
||||
.await
|
||||
.context("fetching formatter packages")?;
|
||||
anyhow::Ok(())
|
||||
@@ -632,6 +631,7 @@ impl Project {
|
||||
plugins: impl Iterator<Item = Arc<str>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let install_dependencies = AllLanguageSettings::get_global(cx).dependency_management;
|
||||
if cfg!(any(test, feature = "test-support")) {
|
||||
self.default_prettier.installed_plugins.extend(plugins);
|
||||
self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance {
|
||||
@@ -746,7 +746,13 @@ impl Project {
|
||||
let installed_plugins = new_plugins.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
|
||||
match install_dependencies {
|
||||
DependencyManagement::Automatic => {
|
||||
install_prettier_packages(fs.as_ref(), new_plugins, node).await?
|
||||
},
|
||||
DependencyManagement::None => Err(anyhow!("No prettier installed"))?,
|
||||
}
|
||||
|
||||
// Save the server file last, so the reinstall need could be determined by the absence of the file.
|
||||
save_prettier_server_file(fs.as_ref()).await?;
|
||||
anyhow::Ok(())
|
||||
|
||||
@@ -44,8 +44,8 @@ use http_client::HttpClient;
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::{
|
||||
language_settings, AllLanguageSettings, FormatOnSave, Formatter, InlayHintKind,
|
||||
LanguageSettings, SelectedFormatter,
|
||||
language_settings, AllLanguageSettings, DependencyManagement, FormatOnSave, Formatter,
|
||||
InlayHintKind, LanguageSettings, SelectedFormatter,
|
||||
},
|
||||
markdown, point_to_lsp, prepare_completion_documentation,
|
||||
proto::{
|
||||
@@ -3058,16 +3058,15 @@ impl Project {
|
||||
|
||||
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
|
||||
let lsp_adapter_delegate = ProjectLspAdapterDelegate::new(self, worktree_handle, cx);
|
||||
let pending_server = match self.languages.create_pending_language_server(
|
||||
let Some(pending_server) = self.languages.create_pending_language_server(
|
||||
stderr_capture.clone(),
|
||||
language.clone(),
|
||||
adapter.clone(),
|
||||
Arc::clone(&worktree_path),
|
||||
lsp_adapter_delegate.clone(),
|
||||
cx,
|
||||
) {
|
||||
Some(pending_server) => pending_server,
|
||||
None => return,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let project_settings = ProjectSettings::get(
|
||||
@@ -3091,7 +3090,7 @@ impl Project {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let result = Self::setup_and_insert_language_server(
|
||||
this.clone(),
|
||||
lsp_adapter_delegate,
|
||||
lsp_adapter_delegate.clone(),
|
||||
override_options,
|
||||
pending_server,
|
||||
adapter.clone(),
|
||||
@@ -3110,6 +3109,22 @@ impl Project {
|
||||
|
||||
Err(err) => {
|
||||
log::error!("failed to start language server {server_name:?}: {err}");
|
||||
let install_dependencies = cx
|
||||
.update(|cx| AllLanguageSettings::get_global(cx).dependency_management)
|
||||
.log_err();
|
||||
if install_dependencies != Some(DependencyManagement::Automatic) {
|
||||
cx.update(move |cx| {
|
||||
lsp_adapter_delegate.show_notification(
|
||||
format!(
|
||||
"failed to start language server {server_name:?}: {err}"
|
||||
)
|
||||
.as_ref(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.log_err();
|
||||
return None;
|
||||
}
|
||||
log::error!("server stderr: {:?}", stderr_capture.lock().take());
|
||||
|
||||
let this = this.upgrade()?;
|
||||
@@ -3119,6 +3134,16 @@ impl Project {
|
||||
if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
|
||||
let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
|
||||
log::error!("Hit {max} reinstallation attempts for {server_name:?}");
|
||||
cx.update(move |cx| {
|
||||
lsp_adapter_delegate.show_notification(
|
||||
format!(
|
||||
"failed to start language server {server_name:?}: {err}"
|
||||
)
|
||||
.as_ref(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.log_err();
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod test;
|
||||
use futures::Future;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use regex::Regex;
|
||||
use std::any::Any;
|
||||
use std::sync::OnceLock;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@@ -328,6 +329,19 @@ pub fn measure<R>(label: &str, f: impl FnOnce() -> R) -> R {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AssetVersion: Sync + Send + 'static {
|
||||
fn description(&self) -> String;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn AssetVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("dyn AssetVersion")
|
||||
.field("description", &self.description())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResultExt<E> {
|
||||
type Ok;
|
||||
|
||||
|
||||
@@ -16,14 +16,17 @@ use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use env_logger::Builder;
|
||||
use fs::{Fs, RealFs};
|
||||
use futures::{future, StreamExt};
|
||||
use futures::{channel::mpsc, future, StreamExt};
|
||||
use git::GitHostingProviderRegistry;
|
||||
use gpui::{
|
||||
Action, App, AppContext, AsyncAppContext, Context, DismissEvent, Global, Task,
|
||||
UpdateGlobal as _, VisualContext,
|
||||
};
|
||||
use image_viewer;
|
||||
use language::LanguageRegistry;
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, DependencyManagement},
|
||||
LanguageRegistry,
|
||||
};
|
||||
use log::LevelFilter;
|
||||
|
||||
use assets::Assets;
|
||||
@@ -448,7 +451,43 @@ fn main() {
|
||||
LanguageRegistry::new(login_shell_env_loaded, cx.background_executor().clone());
|
||||
languages.set_language_server_download_dir(paths::languages_dir().clone());
|
||||
let languages = Arc::new(languages);
|
||||
let node_runtime = RealNodeRuntime::new(client.http_client());
|
||||
|
||||
let (check_server_tx, mut check_server_rx) = mpsc::unbounded();
|
||||
let (can_use_server_tx, mut can_use_server_rx) = mpsc::unbounded();
|
||||
let node_runtime = RealNodeRuntime::new(client.http_client(), check_server_tx, can_use_server_tx);
|
||||
cx.spawn(|cx| {
|
||||
async move {
|
||||
while let Some((asset_version, response)) = check_server_rx.next().await {
|
||||
let install_dependencies = cx
|
||||
.update(|cx| AllLanguageSettings::get_global(cx).dependency_management)
|
||||
.log_err();
|
||||
if install_dependencies == Some(DependencyManagement::Automatic) {
|
||||
response.send(Ok(())).ok();
|
||||
} else {
|
||||
response.send(
|
||||
Err(
|
||||
anyhow!("Could not find {}. Not installing because \"dependency_management\" is set to \"never\".", asset_version.description()))).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|cx| {
|
||||
async move {
|
||||
while let Some(response) = can_use_server_rx.next().await {
|
||||
let install_dependencies = cx
|
||||
.update(|cx| AllLanguageSettings::get_global(cx).dependency_management)
|
||||
.log_err();
|
||||
|
||||
match install_dependencies {
|
||||
Some(DependencyManagement::Automatic) => response.send(true).ok(),
|
||||
_ => response.send(false).ok(),
|
||||
};
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
language::init(cx);
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
@@ -484,6 +523,7 @@ fn main() {
|
||||
});
|
||||
AppState::set_global(Arc::downgrade(&app_state), cx);
|
||||
|
||||
|
||||
auto_update::init(client.http_client(), cx);
|
||||
reliability::init(client.http_client(), installation_id, cx);
|
||||
let prompt_builder = init_common(app_state.clone(), cx);
|
||||
|
||||
Reference in New Issue
Block a user