language: Split LSP installation handling into a separate trait (#38046)
Part of reworking our installation handling to allow for multiple different versions to be handled Release Notes: - Fixed pre-release lsp fetching setting not having an affect until restarting Zed
This commit is contained in:
+215
-178
@@ -28,7 +28,7 @@ use anyhow::{Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use collections::{HashMap, HashSet, IndexSet};
|
||||
use futures::Future;
|
||||
use gpui::{App, AsyncApp, Entity, SharedString, Task};
|
||||
use gpui::{App, AsyncApp, Entity, SharedString};
|
||||
pub use highlight_map::HighlightMap;
|
||||
use http_client::HttpClient;
|
||||
pub use language_registry::{
|
||||
@@ -45,7 +45,6 @@ use settings::WorktreeId;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::num::NonZeroU32;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
@@ -156,6 +155,8 @@ pub struct Location {
|
||||
pub range: Range<Anchor>,
|
||||
}
|
||||
|
||||
type ServerBinaryCache = futures::lock::Mutex<Option<(bool, LanguageServerBinary)>>;
|
||||
|
||||
/// Represents a Language Server, with certain cached sync properties.
|
||||
/// Uses [`LspAdapter`] under the hood, but calls all 'static' methods
|
||||
/// once at startup, and caches the results.
|
||||
@@ -166,7 +167,7 @@ pub struct CachedLspAdapter {
|
||||
language_ids: HashMap<LanguageName, String>,
|
||||
pub adapter: Arc<dyn LspAdapter>,
|
||||
pub reinstall_attempt_count: AtomicU64,
|
||||
cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
|
||||
cached_binary: ServerBinaryCache,
|
||||
}
|
||||
|
||||
impl Debug for CachedLspAdapter {
|
||||
@@ -216,10 +217,16 @@ impl CachedLspAdapter {
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let cached_binary = self.cached_binary.lock().await;
|
||||
let mut cached_binary = self.cached_binary.lock().await;
|
||||
self.adapter
|
||||
.clone()
|
||||
.get_language_server_command(delegate, toolchains, binary_options, cached_binary, cx)
|
||||
.get_language_server_command(
|
||||
delegate,
|
||||
toolchains,
|
||||
binary_options,
|
||||
&mut cached_binary,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -306,128 +313,9 @@ pub trait LspAdapterDelegate: Send + Sync {
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub trait LspAdapter: 'static + Send + Sync {
|
||||
pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller {
|
||||
fn name(&self) -> LanguageServerName;
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
toolchains: Option<Toolchain>,
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
cx: &'a mut AsyncApp,
|
||||
) -> 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 binary_options.allow_path_lookup
|
||||
&& let Some(binary) = self.check_if_user_installed(delegate.as_ref(), toolchains, cx).await {
|
||||
log::debug!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
self.name().0,
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
return Ok(binary);
|
||||
}
|
||||
|
||||
anyhow::ensure!(binary_options.allow_binary_download, "downloading language servers disabled");
|
||||
|
||||
if let Some(cached_binary) = cached_binary.as_ref() {
|
||||
return Ok(cached_binary.clone());
|
||||
}
|
||||
|
||||
let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await else {
|
||||
anyhow::bail!("no language server download dir defined")
|
||||
};
|
||||
|
||||
let mut binary = try_fetch_server_binary(self.as_ref(), &delegate, container_dir.to_path_buf(), cx).await;
|
||||
|
||||
if let Err(error) = binary.as_ref() {
|
||||
if let Some(prev_downloaded_binary) = self
|
||||
.cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
|
||||
.await
|
||||
{
|
||||
log::info!(
|
||||
"failed to fetch newest version of language server {:?}. error: {:?}, falling back to using {:?}",
|
||||
self.name(),
|
||||
error,
|
||||
prev_downloaded_binary.path
|
||||
);
|
||||
binary = Ok(prev_downloaded_binary);
|
||||
} else {
|
||||
delegate.update_status(
|
||||
self.name(),
|
||||
BinaryStatus::Failed {
|
||||
error: format!("{error:?}"),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(binary) = &binary {
|
||||
*cached_binary = Some(binary.clone());
|
||||
}
|
||||
|
||||
binary
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>>;
|
||||
|
||||
fn will_fetch_server(
|
||||
&self,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
_: &mut AsyncApp,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
_version: &(dyn 'static + Send + Any),
|
||||
_container_dir: &PathBuf,
|
||||
_delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary>;
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary>;
|
||||
|
||||
fn process_diagnostics(
|
||||
&self,
|
||||
_: &mut lsp::PublishDiagnosticsParams,
|
||||
@@ -592,40 +480,194 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
|
||||
adapter: &L,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
container_dir: PathBuf,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
if let Some(task) = adapter.will_fetch_server(delegate, cx) {
|
||||
task.await?;
|
||||
pub trait LspInstaller {
|
||||
type BinaryVersion;
|
||||
fn check_if_user_installed(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> impl Future<Output = Option<LanguageServerBinary>> {
|
||||
async { None }
|
||||
}
|
||||
|
||||
let name = adapter.name();
|
||||
log::debug!("fetching latest version of language server {:?}", name.0);
|
||||
delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate);
|
||||
fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
pre_release: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> impl Future<Output = Result<Self::BinaryVersion>>;
|
||||
|
||||
let latest_version = adapter
|
||||
.fetch_latest_server_version(delegate.as_ref(), cx)
|
||||
.await?;
|
||||
fn check_if_version_installed(
|
||||
&self,
|
||||
_version: &Self::BinaryVersion,
|
||||
_container_dir: &PathBuf,
|
||||
_delegate: &dyn LspAdapterDelegate,
|
||||
) -> impl Future<Output = Option<LanguageServerBinary>> {
|
||||
async { None }
|
||||
}
|
||||
|
||||
if let Some(binary) = adapter
|
||||
.check_if_version_installed(latest_version.as_ref(), &container_dir, delegate.as_ref())
|
||||
.await
|
||||
{
|
||||
log::debug!("language server {:?} is already installed", name.0);
|
||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
||||
Ok(binary)
|
||||
} else {
|
||||
log::info!("downloading language server {:?}", name.0);
|
||||
delegate.update_status(adapter.name(), BinaryStatus::Downloading);
|
||||
let binary = adapter
|
||||
.fetch_server_binary(latest_version, container_dir, delegate.as_ref())
|
||||
.await;
|
||||
fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Self::BinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> impl Future<Output = Result<LanguageServerBinary>>;
|
||||
|
||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
||||
binary
|
||||
fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> impl Future<Output = Option<LanguageServerBinary>>;
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub trait DynLspInstaller {
|
||||
async fn try_fetch_server_binary(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
container_dir: PathBuf,
|
||||
pre_release: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LanguageServerBinary>;
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
toolchains: Option<Toolchain>,
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
cached_binary: &'a mut Option<(bool, LanguageServerBinary)>,
|
||||
cx: &'a mut AsyncApp,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>>;
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl<LI, BinaryVersion> DynLspInstaller for LI
|
||||
where
|
||||
LI: LspInstaller<BinaryVersion = BinaryVersion> + LspAdapter,
|
||||
{
|
||||
async fn try_fetch_server_binary(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
container_dir: PathBuf,
|
||||
pre_release: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let name = self.name();
|
||||
|
||||
log::debug!("fetching latest version of language server {:?}", name.0);
|
||||
delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate);
|
||||
|
||||
let latest_version = self
|
||||
.fetch_latest_server_version(delegate.as_ref(), pre_release, cx)
|
||||
.await?;
|
||||
|
||||
if let Some(binary) = self
|
||||
.check_if_version_installed(&latest_version, &container_dir, delegate.as_ref())
|
||||
.await
|
||||
{
|
||||
log::debug!("language server {:?} is already installed", name.0);
|
||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
||||
Ok(binary)
|
||||
} else {
|
||||
log::debug!("downloading language server {:?}", name.0);
|
||||
delegate.update_status(name.clone(), BinaryStatus::Downloading);
|
||||
let binary = self
|
||||
.fetch_server_binary(latest_version, container_dir, delegate.as_ref())
|
||||
.await;
|
||||
|
||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
||||
binary
|
||||
}
|
||||
}
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
toolchain: Option<Toolchain>,
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
cached_binary: &'a mut Option<(bool, LanguageServerBinary)>,
|
||||
cx: &'a mut AsyncApp,
|
||||
) -> 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 binary_options.allow_path_lookup
|
||||
&& let Some(binary) = self
|
||||
.check_if_user_installed(delegate.as_ref(), toolchain, cx)
|
||||
.await
|
||||
{
|
||||
log::info!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
self.name().0,
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
return Ok(binary);
|
||||
}
|
||||
|
||||
anyhow::ensure!(
|
||||
binary_options.allow_binary_download,
|
||||
"downloading language servers disabled"
|
||||
);
|
||||
|
||||
if let Some((pre_release, cached_binary)) = cached_binary
|
||||
&& *pre_release == binary_options.pre_release
|
||||
{
|
||||
return Ok(cached_binary.clone());
|
||||
}
|
||||
|
||||
let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await
|
||||
else {
|
||||
anyhow::bail!("no language server download dir defined")
|
||||
};
|
||||
|
||||
let mut binary = self
|
||||
.try_fetch_server_binary(
|
||||
&delegate,
|
||||
container_dir.to_path_buf(),
|
||||
binary_options.pre_release,
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(error) = binary.as_ref() {
|
||||
if let Some(prev_downloaded_binary) = self
|
||||
.cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
|
||||
.await
|
||||
{
|
||||
log::info!(
|
||||
"failed to fetch newest version of language server {:?}. \
|
||||
error: {:?}, falling back to using {:?}",
|
||||
self.name(),
|
||||
error,
|
||||
prev_downloaded_binary.path
|
||||
);
|
||||
binary = Ok(prev_downloaded_binary);
|
||||
} else {
|
||||
delegate.update_status(
|
||||
self.name(),
|
||||
BinaryStatus::Failed {
|
||||
error: format!("{error:?}"),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(binary) = &binary {
|
||||
*cached_binary = Some((binary_options.pre_release, binary.clone()));
|
||||
}
|
||||
|
||||
binary
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2195,10 +2237,16 @@ impl Default for FakeLspAdapter {
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for FakeLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName(self.name.into())
|
||||
impl LspInstaller for FakeLspAdapter {
|
||||
type BinaryVersion = ();
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<Self::BinaryVersion> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -2210,28 +2258,9 @@ impl LspAdapter for FakeLspAdapter {
|
||||
Some(self.language_server_binary.clone())
|
||||
}
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Arc<dyn LspAdapterDelegate>,
|
||||
_: Option<Toolchain>,
|
||||
_: LanguageServerBinaryOptions,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
_: &'a mut AsyncApp,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
async move { Ok(self.language_server_binary.clone()) }.boxed_local()
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: (),
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -2245,6 +2274,14 @@ impl LspAdapter for FakeLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for FakeLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName(self.name.into())
|
||||
}
|
||||
|
||||
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
self.disk_based_diagnostics_sources.clone()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::any::Any;
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
@@ -11,8 +10,8 @@ use extension::{Extension, ExtensionLanguageServerProxy, WorktreeDelegate};
|
||||
use futures::{Future, FutureExt, future::join_all};
|
||||
use gpui::{App, AppContext, AsyncApp, Task};
|
||||
use language::{
|
||||
BinaryStatus, CodeLabel, HighlightId, Language, LanguageName, LspAdapter, LspAdapterDelegate,
|
||||
Toolchain,
|
||||
BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,
|
||||
LspAdapterDelegate, Toolchain,
|
||||
};
|
||||
use lsp::{
|
||||
CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
|
||||
@@ -155,17 +154,13 @@ impl ExtensionLspAdapter {
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for ExtensionLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
self.language_server_id.clone()
|
||||
}
|
||||
|
||||
impl DynLspInstaller for ExtensionLspAdapter {
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
_: Option<Toolchain>,
|
||||
_: LanguageServerBinaryOptions,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
_: &'a mut Option<(bool, LanguageServerBinary)>,
|
||||
_: &'a mut AsyncApp,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
async move {
|
||||
@@ -205,29 +200,21 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||
.boxed_local()
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
async fn try_fetch_server_binary(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
unreachable!("get_language_server_command is overridden")
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
unreachable!("get_language_server_command is overridden")
|
||||
}
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
unreachable!("get_language_server_command is overridden")
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for ExtensionLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
self.language_server_id.clone()
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
|
||||
+34
-38
@@ -5,11 +5,10 @@ use gpui::{App, AsyncApp};
|
||||
use http_client::github::{AssetKind, GitHubLspBinaryVersion, latest_github_release};
|
||||
pub use language::*;
|
||||
use lsp::{InitializeParams, LanguageServerBinary, LanguageServerName};
|
||||
use project::{lsp_store::clangd_ext, project_settings::ProjectSettings};
|
||||
use project::lsp_store::clangd_ext;
|
||||
use serde_json::json;
|
||||
use settings::Settings as _;
|
||||
use smol::fs;
|
||||
use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
|
||||
use std::{env::consts, path::PathBuf, sync::Arc};
|
||||
use util::{ResultExt, fs::remove_matching, maybe, merge_json_value_into};
|
||||
|
||||
use crate::github_download::{GithubBinaryMetadata, download_server_binary};
|
||||
@@ -20,42 +19,18 @@ impl CLspAdapter {
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("clangd");
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl super::LspAdapter for CLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: Vec::new(),
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
impl LspInstaller for CLspAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let release = latest_github_release(
|
||||
"clangd/clangd",
|
||||
true,
|
||||
ProjectSettings::try_read_global(cx, |s| {
|
||||
s.lsp.get(&Self::SERVER_NAME)?.fetch.as_ref()?.pre_release
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or(false),
|
||||
delegate.http_client(),
|
||||
)
|
||||
.await?;
|
||||
pre_release: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<GitHubLspBinaryVersion> {
|
||||
let release =
|
||||
latest_github_release("clangd/clangd", true, pre_release, delegate.http_client())
|
||||
.await?;
|
||||
let os_suffix = match consts::OS {
|
||||
"macos" => "mac",
|
||||
"linux" => "linux",
|
||||
@@ -73,12 +48,26 @@ impl super::LspAdapter for CLspAdapter {
|
||||
url: asset.browser_download_url.clone(),
|
||||
digest: asset.digest.clone(),
|
||||
};
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: Vec::new(),
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: GitHubLspBinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -86,7 +75,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||
name,
|
||||
url,
|
||||
digest: expected_digest,
|
||||
} = *version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
} = version;
|
||||
let version_dir = container_dir.join(format!("clangd_{name}"));
|
||||
let binary_path = version_dir.join("bin/clangd");
|
||||
|
||||
@@ -157,6 +146,13 @@ impl super::LspAdapter for CLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl super::LspAdapter for CLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
|
||||
+22
-22
@@ -2,14 +2,13 @@ use anyhow::{Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::AsyncApp;
|
||||
use language::{LspAdapter, LspAdapterDelegate, Toolchain};
|
||||
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::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -34,10 +33,18 @@ impl CssLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for CssLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("vscode-css-language-server".into())
|
||||
impl LspInstaller for CssLspAdapter {
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -58,25 +65,12 @@ impl LspAdapter for CssLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: String,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
@@ -95,11 +89,10 @@ impl LspAdapter for CssLspAdapter {
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
version: &String,
|
||||
container_dir: &PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
@@ -130,6 +123,13 @@ impl LspAdapter for CssLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for CssLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("vscode-css-language-server".into())
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
|
||||
+38
-48
@@ -5,17 +5,17 @@ use futures::StreamExt;
|
||||
use gpui::{App, AsyncApp, Task};
|
||||
use http_client::github::latest_github_release;
|
||||
pub use language::*;
|
||||
use language::{LanguageToolchainStore, LspAdapterDelegate, LspInstaller};
|
||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||
|
||||
use regex::Regex;
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
ffi::{OsStr, OsString},
|
||||
ops::Range,
|
||||
path::PathBuf,
|
||||
path::{Path, PathBuf},
|
||||
process::Output,
|
||||
str,
|
||||
sync::{
|
||||
@@ -50,17 +50,32 @@ const BINARY: &str = if cfg!(target_os = "windows") {
|
||||
"gopls"
|
||||
};
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl super::LspAdapter for GoLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
impl LspInstaller for GoLspAdapter {
|
||||
type BinaryVersion = Option<String>;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
_: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Option<String>> {
|
||||
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const NOTIFICATION_MESSAGE: &str =
|
||||
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||
|
||||
if delegate.which("go".as_ref()).await.is_none() {
|
||||
if DID_SHOW_NOTIFICATION
|
||||
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})?
|
||||
}
|
||||
anyhow::bail!("cannot install gopls");
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -70,7 +85,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
release.tag_name
|
||||
);
|
||||
}
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -87,36 +102,9 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
fn will_fetch_server(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const NOTIFICATION_MESSAGE: &str =
|
||||
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||
|
||||
let delegate = delegate.clone();
|
||||
Some(cx.spawn(async move |cx| {
|
||||
if delegate.which("go".as_ref()).await.is_none() {
|
||||
if DID_SHOW_NOTIFICATION
|
||||
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})?
|
||||
}
|
||||
anyhow::bail!("cannot install gopls");
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
version: Option<String>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -127,10 +115,8 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
.await
|
||||
.context("failed to get go version via `go version` command`")?;
|
||||
let go_version = parse_version_output(&go_version_output)?;
|
||||
let version = version.downcast::<Option<String>>().unwrap();
|
||||
let this = *self;
|
||||
|
||||
if let Some(version) = *version {
|
||||
if let Some(version) = version {
|
||||
let binary_path = container_dir.join(format!("gopls_{version}_go_{go_version}"));
|
||||
if let Ok(metadata) = fs::metadata(&binary_path).await
|
||||
&& metadata.is_file()
|
||||
@@ -146,10 +132,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
env: None,
|
||||
});
|
||||
}
|
||||
} else if let Some(path) = this
|
||||
.cached_server_binary(container_dir.clone(), delegate)
|
||||
.await
|
||||
{
|
||||
} else if let Some(path) = get_cached_server_binary(&container_dir).await {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
@@ -195,7 +178,14 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
get_cached_server_binary(&container_dir).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for GoLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
@@ -442,10 +432,10 @@ fn parse_version_output(output: &Output) -> Result<&str> {
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
async fn get_cached_server_binary(container_dir: &Path) -> Option<LanguageServerBinary> {
|
||||
maybe!(async {
|
||||
let mut last_binary_path = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
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_file()
|
||||
|
||||
@@ -9,7 +9,7 @@ use gpui::{App, AsyncApp, Task};
|
||||
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||
use language::{
|
||||
ContextProvider, LanguageName, LanguageRegistry, LocalFile as _, LspAdapter,
|
||||
LspAdapterDelegate, Toolchain,
|
||||
LspAdapterDelegate, LspInstaller, Toolchain,
|
||||
};
|
||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||
@@ -22,7 +22,6 @@ use smol::{
|
||||
lock::RwLock,
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
env::consts,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
@@ -293,10 +292,18 @@ fn generate_inspector_style_schema() -> serde_json_lenient::Value {
|
||||
serde_json_lenient::to_value(schema).unwrap()
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for JsonLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("json-language-server".into())
|
||||
impl LspInstaller for JsonLspAdapter {
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -317,25 +324,12 @@ impl LspAdapter for JsonLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
version: &String,
|
||||
container_dir: &PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
@@ -361,11 +355,10 @@ impl LspAdapter for JsonLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: String,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
@@ -389,6 +382,13 @@ impl LspAdapter for JsonLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for JsonLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("json-language-server".into())
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
@@ -483,17 +483,15 @@ impl NodeVersionAdapter {
|
||||
LanguageServerName::new_static("package-version-server");
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for NodeVersionAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
impl LspInstaller for NodeVersionAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<GitHubLspBinaryVersion> {
|
||||
let release = latest_github_release(
|
||||
"zed-industries/package-version-server",
|
||||
true,
|
||||
@@ -518,11 +516,11 @@ impl LspAdapter for NodeVersionAdapter {
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
Ok(Box::new(GitHubLspBinaryVersion {
|
||||
Ok(GitHubLspBinaryVersion {
|
||||
name: release.tag_name,
|
||||
url: asset.browser_download_url.clone(),
|
||||
digest: asset.digest.clone(),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -541,11 +539,11 @@ impl LspAdapter for NodeVersionAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: GitHubLspBinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let version = &latest_version;
|
||||
let destination_path = container_dir.join(format!(
|
||||
"{}-{}{}",
|
||||
Self::SERVER_NAME,
|
||||
@@ -595,6 +593,13 @@ impl LspAdapter for NodeVersionAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for NodeVersionAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_version_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
maybe!(async {
|
||||
let mut last = None;
|
||||
|
||||
+337
-320
@@ -6,7 +6,7 @@ use futures::{AsyncBufReadExt, StreamExt as _};
|
||||
use gpui::{App, AsyncApp, SharedString, Task};
|
||||
use http_client::github::{AssetKind, GitHubLspBinaryVersion, latest_github_release};
|
||||
use language::language_settings::language_settings;
|
||||
use language::{ContextLocation, LanguageToolchainStore};
|
||||
use language::{ContextLocation, LanguageToolchainStore, LspInstaller};
|
||||
use language::{ContextProvider, LspAdapter, LspAdapterDelegate};
|
||||
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
|
||||
use language::{Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata};
|
||||
@@ -27,7 +27,6 @@ use util::fs::{make_file_executable, remove_matching};
|
||||
use parking_lot::Mutex;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
ffi::OsString,
|
||||
fmt::Write,
|
||||
@@ -159,11 +158,44 @@ impl LspAdapter for TyLspAdapter {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
toolchain: Option<Toolchain>,
|
||||
_cx: &mut AsyncApp,
|
||||
) -> Result<Value> {
|
||||
let mut ret = json!({});
|
||||
if let Some(toolchain) = toolchain.and_then(|toolchain| {
|
||||
serde_json::from_value::<PythonEnvironment>(toolchain.as_json).ok()
|
||||
}) {
|
||||
_ = maybe!({
|
||||
let uri = url::Url::from_file_path(toolchain.executable?).ok()?;
|
||||
let sys_prefix = toolchain.prefix.clone()?;
|
||||
let environment = json!({
|
||||
"executable": {
|
||||
"uri": uri,
|
||||
"sysPrefix": sys_prefix
|
||||
}
|
||||
});
|
||||
ret.as_object_mut()?.insert(
|
||||
"pythonExtension".into(),
|
||||
json!({ "activeEnvironment": environment }),
|
||||
);
|
||||
Some(())
|
||||
});
|
||||
}
|
||||
Ok(json!({"ty": ret}))
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for TyLspAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<Self::BinaryVersion> {
|
||||
let release =
|
||||
latest_github_release("astral-sh/ty", true, true, delegate.http_client()).await?;
|
||||
let (_, asset_name) = Self::build_asset_name()?;
|
||||
@@ -172,16 +204,16 @@ impl LspAdapter for TyLspAdapter {
|
||||
.into_iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
Ok(Box::new(GitHubLspBinaryVersion {
|
||||
Ok(GitHubLspBinaryVersion {
|
||||
name: release.tag_name,
|
||||
url: asset.browser_download_url,
|
||||
digest: asset.digest,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: Self::BinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -189,7 +221,7 @@ impl LspAdapter for TyLspAdapter {
|
||||
name,
|
||||
url,
|
||||
digest: expected_digest,
|
||||
} = *latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
} = latest_version;
|
||||
let destination_path = container_dir.join(format!("ty-{name}"));
|
||||
let server_path = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place.
|
||||
@@ -293,35 +325,6 @@ impl LspAdapter for TyLspAdapter {
|
||||
.await
|
||||
.log_err()
|
||||
}
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
toolchain: Option<Toolchain>,
|
||||
_cx: &mut AsyncApp,
|
||||
) -> Result<Value> {
|
||||
let mut ret = json!({});
|
||||
if let Some(toolchain) = toolchain.and_then(|toolchain| {
|
||||
serde_json::from_value::<PythonEnvironment>(toolchain.as_json).ok()
|
||||
}) {
|
||||
_ = maybe!({
|
||||
let uri = url::Url::from_file_path(toolchain.executable?).ok()?;
|
||||
let sys_prefix = toolchain.prefix.clone()?;
|
||||
let environment = json!({
|
||||
"executable": {
|
||||
"uri": uri,
|
||||
"sysPrefix": sys_prefix
|
||||
}
|
||||
});
|
||||
ret.as_object_mut()?.insert(
|
||||
"pythonExtension".into(),
|
||||
json!({ "activeEnvironment": environment }),
|
||||
);
|
||||
Some(())
|
||||
});
|
||||
}
|
||||
Ok(json!({"ty": ret}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PyrightLspAdapter {
|
||||
@@ -359,114 +362,6 @@ impl LspAdapter for PyrightLspAdapter {
|
||||
})))
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(pyright_bin) = delegate.which("pyright-langserver".as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: pyright_bin,
|
||||
env: Some(env),
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
let node = delegate.which("node".as_ref()).await?;
|
||||
let (node_modules_path, _) = delegate
|
||||
.npm_package_installed_version(Self::SERVER_NAME.as_ref())
|
||||
.await
|
||||
.log_err()??;
|
||||
|
||||
let path = node_modules_path.join(NODE_MODULE_RELATIVE_SERVER_PATH);
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: node,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version(Self::SERVER_NAME.as_ref())
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[(Self::SERVER_NAME.as_ref(), latest_version.as_str())],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
container_dir: &PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(
|
||||
Self::SERVER_NAME.as_ref(),
|
||||
&server_path,
|
||||
container_dir,
|
||||
VersionStrategy::Latest(version),
|
||||
)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
None
|
||||
} else {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let mut binary = get_cached_server_binary(container_dir, &self.node).await?;
|
||||
binary.env = Some(delegate.shell_env().await);
|
||||
Some(binary)
|
||||
}
|
||||
|
||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||
process_pyright_completions(items);
|
||||
}
|
||||
@@ -616,6 +511,115 @@ impl LspAdapter for PyrightLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for PyrightLspAdapter {
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version(Self::SERVER_NAME.as_ref())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(pyright_bin) = delegate.which("pyright-langserver".as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: pyright_bin,
|
||||
env: Some(env),
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
let node = delegate.which("node".as_ref()).await?;
|
||||
let (node_modules_path, _) = delegate
|
||||
.npm_package_installed_version(Self::SERVER_NAME.as_ref())
|
||||
.await
|
||||
.log_err()??;
|
||||
|
||||
let path = node_modules_path.join(NODE_MODULE_RELATIVE_SERVER_PATH);
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: node,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Self::BinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[(Self::SERVER_NAME.as_ref(), latest_version.as_str())],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &Self::BinaryVersion,
|
||||
container_dir: &PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(
|
||||
Self::SERVER_NAME.as_ref(),
|
||||
&server_path,
|
||||
container_dir,
|
||||
VersionStrategy::Latest(version),
|
||||
)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
None
|
||||
} else {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let mut binary = get_cached_server_binary(container_dir, &self.node).await?;
|
||||
binary.env = Some(delegate.shell_env().await);
|
||||
Some(binary)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &NodeRuntime,
|
||||
@@ -1333,101 +1337,6 @@ impl LspAdapter for PyLspAdapter {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
toolchain: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(pylsp_bin) = delegate.which(Self::SERVER_NAME.as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp_bin,
|
||||
env: Some(env),
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
let toolchain = toolchain?;
|
||||
let pylsp_path = Path::new(toolchain.path.as_ref()).parent()?.join("pylsp");
|
||||
pylsp_path.exists().then(|| LanguageServerBinary {
|
||||
path: toolchain.path.to_string().into(),
|
||||
arguments: vec![pylsp_path.into()],
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(()) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
|
||||
let pip_path = venv.join(BINARY_DIR).join("pip3");
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"python-lsp-server installation failed"
|
||||
);
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server[all]")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"python-lsp-server[all] installation failed"
|
||||
);
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path)
|
||||
.arg("install")
|
||||
.arg("pylsp-mypy")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"pylsp-mypy installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn process_completions(&self, _items: &mut [lsp::CompletionItem]) {}
|
||||
|
||||
async fn label_for_completion(
|
||||
@@ -1559,6 +1468,105 @@ impl LspAdapter for PyLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for PyLspAdapter {
|
||||
type BinaryVersion = ();
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
toolchain: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(pylsp_bin) = delegate.which(Self::SERVER_NAME.as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp_bin,
|
||||
env: Some(env),
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
let toolchain = toolchain?;
|
||||
let pylsp_path = Path::new(toolchain.path.as_ref()).parent()?.join("pylsp");
|
||||
pylsp_path.exists().then(|| LanguageServerBinary {
|
||||
path: toolchain.path.to_string().into(),
|
||||
arguments: vec![pylsp_path.into()],
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: (),
|
||||
_: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
|
||||
let pip_path = venv.join(BINARY_DIR).join("pip3");
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"python-lsp-server installation failed"
|
||||
);
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server[all]")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"python-lsp-server[all] installation failed"
|
||||
);
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path)
|
||||
.arg("install")
|
||||
.arg("pylsp-mypy")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"pylsp-mypy installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BasedPyrightLspAdapter {
|
||||
python_venv_base: OnceCell<Result<Arc<Path>, String>>,
|
||||
}
|
||||
@@ -1642,80 +1650,6 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||
})))
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
toolchain: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(bin) = delegate.which(Self::BINARY_NAME.as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: bin,
|
||||
env: Some(env),
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
let path = Path::new(toolchain?.path.as_ref())
|
||||
.parent()?
|
||||
.join(Self::BINARY_NAME);
|
||||
path.exists().then(|| LanguageServerBinary {
|
||||
path,
|
||||
arguments: vec!["--stdio".into()],
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(()) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_latest_version: Box<dyn 'static + Send + Any>,
|
||||
_container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
|
||||
let pip_path = venv.join(BINARY_DIR).join("pip3");
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("basedpyright")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"basedpyright installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||
process_pyright_completions(items);
|
||||
}
|
||||
@@ -1878,6 +1812,85 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for BasedPyrightLspAdapter {
|
||||
type BinaryVersion = ();
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
toolchain: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(bin) = delegate.which(Self::BINARY_NAME.as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: bin,
|
||||
env: Some(env),
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
let path = Path::new(toolchain?.path.as_ref())
|
||||
.parent()?
|
||||
.join(Self::BINARY_NAME);
|
||||
path.exists().then(|| LanguageServerBinary {
|
||||
path,
|
||||
arguments: vec!["--stdio".into()],
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_latest_version: (),
|
||||
_container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
|
||||
let pip_path = venv.join(BINARY_DIR).join("pip3");
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("basedpyright")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"basedpyright installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RuffLspAdapter {
|
||||
fs: Arc<dyn Fs>,
|
||||
}
|
||||
@@ -1934,7 +1947,10 @@ impl LspAdapter for RuffLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for RuffLspAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
@@ -1968,8 +1984,9 @@ impl LspAdapter for RuffLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<GitHubLspBinaryVersion> {
|
||||
let release =
|
||||
latest_github_release("astral-sh/ruff", true, false, delegate.http_client()).await?;
|
||||
let (_, asset_name) = Self::build_asset_name()?;
|
||||
@@ -1978,16 +1995,16 @@ impl LspAdapter for RuffLspAdapter {
|
||||
.into_iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
Ok(Box::new(GitHubLspBinaryVersion {
|
||||
Ok(GitHubLspBinaryVersion {
|
||||
name: release.tag_name,
|
||||
url: asset.browser_download_url,
|
||||
digest: asset.digest,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: GitHubLspBinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -1995,7 +2012,7 @@ impl LspAdapter for RuffLspAdapter {
|
||||
name,
|
||||
url,
|
||||
digest: expected_digest,
|
||||
} = *latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
} = latest_version;
|
||||
let destination_path = container_dir.join(format!("ruff-{name}"));
|
||||
let server_path = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path
|
||||
|
||||
+155
-156
@@ -16,7 +16,6 @@ use smol::fs::{self};
|
||||
use std::fmt::Display;
|
||||
use std::ops::Range;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, LazyLock},
|
||||
@@ -108,161 +107,6 @@ impl LspAdapter for RustLspAdapter {
|
||||
SERVER_NAME
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let path = delegate.which("rust-analyzer".as_ref()).await?;
|
||||
let env = delegate.shell_env().await;
|
||||
|
||||
// It is surprisingly common for ~/.cargo/bin/rust-analyzer to be a symlink to
|
||||
// /usr/bin/rust-analyzer that fails when you run it; so we need to test it.
|
||||
log::info!("found rust-analyzer in PATH. trying to run `rust-analyzer --help`");
|
||||
let result = delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: vec!["--help".into()],
|
||||
env: Some(env.clone()),
|
||||
})
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
log::debug!(
|
||||
"failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {}",
|
||||
path,
|
||||
err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
env: Some(env),
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let release = latest_github_release(
|
||||
"rust-lang/rust-analyzer",
|
||||
true,
|
||||
ProjectSettings::try_read_global(cx, |s| {
|
||||
s.lsp.get(&SERVER_NAME)?.fetch.as_ref()?.pre_release
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or(false),
|
||||
delegate.http_client(),
|
||||
)
|
||||
.await?;
|
||||
let asset_name = Self::build_asset_name();
|
||||
let asset = release
|
||||
.assets
|
||||
.into_iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
Ok(Box::new(GitHubLspBinaryVersion {
|
||||
name: release.tag_name,
|
||||
url: asset.browser_download_url,
|
||||
digest: asset.digest,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let GitHubLspBinaryVersion {
|
||||
name,
|
||||
url,
|
||||
digest: expected_digest,
|
||||
} = *version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let destination_path = container_dir.join(format!("rust-analyzer-{name}"));
|
||||
let server_path = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place.
|
||||
AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe
|
||||
};
|
||||
|
||||
let binary = LanguageServerBinary {
|
||||
path: server_path.clone(),
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
};
|
||||
|
||||
let metadata_path = destination_path.with_extension("metadata");
|
||||
let metadata = GithubBinaryMetadata::read_from_file(&metadata_path)
|
||||
.await
|
||||
.ok();
|
||||
if let Some(metadata) = metadata {
|
||||
let validity_check = async || {
|
||||
delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: server_path.clone(),
|
||||
arguments: vec!["--version".into()],
|
||||
env: None,
|
||||
})
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
log::warn!("Unable to run {server_path:?} asset, redownloading: {err}",)
|
||||
})
|
||||
};
|
||||
if let (Some(actual_digest), Some(expected_digest)) =
|
||||
(&metadata.digest, &expected_digest)
|
||||
{
|
||||
if actual_digest == expected_digest {
|
||||
if validity_check().await.is_ok() {
|
||||
return Ok(binary);
|
||||
}
|
||||
} else {
|
||||
log::info!(
|
||||
"SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}"
|
||||
);
|
||||
}
|
||||
} else if validity_check().await.is_ok() {
|
||||
return Ok(binary);
|
||||
}
|
||||
}
|
||||
|
||||
download_server_binary(
|
||||
delegate,
|
||||
&url,
|
||||
expected_digest.as_deref(),
|
||||
&destination_path,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)
|
||||
.await?;
|
||||
make_file_executable(&server_path).await?;
|
||||
remove_matching(&container_dir, |path| path != destination_path).await;
|
||||
GithubBinaryMetadata::write_to_file(
|
||||
&GithubBinaryMetadata {
|
||||
metadata_version: 1,
|
||||
digest: expected_digest,
|
||||
},
|
||||
&metadata_path,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: server_path,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
vec![CARGO_DIAGNOSTICS_SOURCE_NAME.to_owned()]
|
||||
}
|
||||
@@ -518,6 +362,161 @@ impl LspAdapter for RustLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for RustLspAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let path = delegate.which("rust-analyzer".as_ref()).await?;
|
||||
let env = delegate.shell_env().await;
|
||||
|
||||
// It is surprisingly common for ~/.cargo/bin/rust-analyzer to be a symlink to
|
||||
// /usr/bin/rust-analyzer that fails when you run it; so we need to test it.
|
||||
log::info!("found rust-analyzer in PATH. trying to run `rust-analyzer --help`");
|
||||
let result = delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: vec!["--help".into()],
|
||||
env: Some(env.clone()),
|
||||
})
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
log::debug!(
|
||||
"failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {}",
|
||||
path,
|
||||
err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
env: Some(env),
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
pre_release: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<GitHubLspBinaryVersion> {
|
||||
let release = latest_github_release(
|
||||
"rust-lang/rust-analyzer",
|
||||
true,
|
||||
pre_release,
|
||||
delegate.http_client(),
|
||||
)
|
||||
.await?;
|
||||
let asset_name = Self::build_asset_name();
|
||||
let asset = release
|
||||
.assets
|
||||
.into_iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
Ok(GitHubLspBinaryVersion {
|
||||
name: release.tag_name,
|
||||
url: asset.browser_download_url,
|
||||
digest: asset.digest,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: GitHubLspBinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let GitHubLspBinaryVersion {
|
||||
name,
|
||||
url,
|
||||
digest: expected_digest,
|
||||
} = version;
|
||||
let destination_path = container_dir.join(format!("rust-analyzer-{name}"));
|
||||
let server_path = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place.
|
||||
AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe
|
||||
};
|
||||
|
||||
let binary = LanguageServerBinary {
|
||||
path: server_path.clone(),
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
};
|
||||
|
||||
let metadata_path = destination_path.with_extension("metadata");
|
||||
let metadata = GithubBinaryMetadata::read_from_file(&metadata_path)
|
||||
.await
|
||||
.ok();
|
||||
if let Some(metadata) = metadata {
|
||||
let validity_check = async || {
|
||||
delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: server_path.clone(),
|
||||
arguments: vec!["--version".into()],
|
||||
env: None,
|
||||
})
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
log::warn!("Unable to run {server_path:?} asset, redownloading: {err}",)
|
||||
})
|
||||
};
|
||||
if let (Some(actual_digest), Some(expected_digest)) =
|
||||
(&metadata.digest, &expected_digest)
|
||||
{
|
||||
if actual_digest == expected_digest {
|
||||
if validity_check().await.is_ok() {
|
||||
return Ok(binary);
|
||||
}
|
||||
} else {
|
||||
log::info!(
|
||||
"SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}"
|
||||
);
|
||||
}
|
||||
} else if validity_check().await.is_ok() {
|
||||
return Ok(binary);
|
||||
}
|
||||
}
|
||||
|
||||
download_server_binary(
|
||||
delegate,
|
||||
&url,
|
||||
expected_digest.as_deref(),
|
||||
&destination_path,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)
|
||||
.await?;
|
||||
make_file_executable(&server_path).await?;
|
||||
remove_matching(&container_dir, |path| path != destination_path).await;
|
||||
GithubBinaryMetadata::write_to_file(
|
||||
&GithubBinaryMetadata {
|
||||
metadata_version: 1,
|
||||
digest: expected_digest,
|
||||
},
|
||||
&metadata_path,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: server_path,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustContextProvider;
|
||||
|
||||
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
||||
|
||||
@@ -3,14 +3,13 @@ use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use futures::StreamExt;
|
||||
use gpui::AsyncApp;
|
||||
use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
|
||||
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::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -41,10 +40,18 @@ impl TailwindLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for TailwindLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
impl LspInstaller for TailwindLspAdapter {
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -63,25 +70,12 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: String,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
@@ -100,11 +94,10 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
version: &String,
|
||||
container_dir: &PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
@@ -135,6 +128,13 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for TailwindLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
|
||||
+111
-105
@@ -7,7 +7,7 @@ use gpui::{App, AppContext, AsyncApp, Task};
|
||||
use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
|
||||
use language::{
|
||||
ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter,
|
||||
LspAdapterDelegate, Toolchain,
|
||||
LspAdapterDelegate, LspInstaller, Toolchain,
|
||||
};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||
@@ -15,7 +15,6 @@ use project::{Fs, lsp_store::language_server_settings};
|
||||
use serde_json::{Value, json};
|
||||
use smol::{fs, lock::RwLock, stream::StreamExt};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
@@ -555,38 +554,35 @@ impl TypeScriptLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeScriptVersions {
|
||||
pub struct TypeScriptVersions {
|
||||
typescript_version: String,
|
||||
server_version: String,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for TypeScriptLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
impl LspInstaller for TypeScriptLspAdapter {
|
||||
type BinaryVersion = TypeScriptVersions;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(TypeScriptVersions {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<TypeScriptVersions> {
|
||||
Ok(TypeScriptVersions {
|
||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||
server_version: self
|
||||
.node
|
||||
.npm_package_latest_version("typescript-language-server")
|
||||
.await?,
|
||||
}) as Box<_>)
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
version: &TypeScriptVersions,
|
||||
container_dir: &PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<TypeScriptVersions>().unwrap();
|
||||
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
@@ -612,11 +608,10 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: TypeScriptVersions,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
||||
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
||||
|
||||
self.node
|
||||
@@ -649,6 +644,13 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for TypeScriptLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
Some(vec![
|
||||
@@ -815,6 +817,99 @@ impl EsLintLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
impl LspInstaller for EsLintLspAdapter {
|
||||
type BinaryVersion = GitHubLspBinaryVersion;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_delegate: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<GitHubLspBinaryVersion> {
|
||||
let url = build_asset_url(
|
||||
"zed-industries/vscode-eslint",
|
||||
Self::CURRENT_VERSION_TAG_NAME,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)?;
|
||||
|
||||
Ok(GitHubLspBinaryVersion {
|
||||
name: Self::CURRENT_VERSION.into(),
|
||||
digest: None,
|
||||
url,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: GitHubLspBinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let destination_path = Self::build_destination_path(&container_dir);
|
||||
let server_path = destination_path.join(Self::SERVER_PATH);
|
||||
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
remove_matching(&container_dir, |_| true).await;
|
||||
|
||||
download_server_binary(
|
||||
delegate,
|
||||
&version.url,
|
||||
None,
|
||||
&destination_path,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut dir = fs::read_dir(&destination_path).await?;
|
||||
let first = dir.next().await.context("missing first file")??;
|
||||
let repo_root = destination_path.join("vscode-eslint");
|
||||
fs::rename(first.path(), &repo_root).await?;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
handle_symlink(
|
||||
repo_root.join("$shared"),
|
||||
repo_root.join("client").join("src").join("shared"),
|
||||
)
|
||||
.await?;
|
||||
handle_symlink(
|
||||
repo_root.join("$shared"),
|
||||
repo_root.join("server").join("src").join("shared"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.node
|
||||
.run_npm_subcommand(&repo_root, "install", &[])
|
||||
.await?;
|
||||
|
||||
self.node
|
||||
.run_npm_subcommand(&repo_root, "run-script", &["compile"])
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path =
|
||||
Self::build_destination_path(&container_dir).join(EsLintLspAdapter::SERVER_PATH);
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for EsLintLspAdapter {
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
@@ -886,95 +981,6 @@ impl LspAdapter for EsLintLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_delegate: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let url = build_asset_url(
|
||||
"zed-industries/vscode-eslint",
|
||||
Self::CURRENT_VERSION_TAG_NAME,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)?;
|
||||
|
||||
Ok(Box::new(GitHubLspBinaryVersion {
|
||||
name: Self::CURRENT_VERSION.into(),
|
||||
digest: None,
|
||||
url,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let destination_path = Self::build_destination_path(&container_dir);
|
||||
let server_path = destination_path.join(Self::SERVER_PATH);
|
||||
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
remove_matching(&container_dir, |_| true).await;
|
||||
|
||||
download_server_binary(
|
||||
delegate,
|
||||
&version.url,
|
||||
None,
|
||||
&destination_path,
|
||||
Self::GITHUB_ASSET_KIND,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut dir = fs::read_dir(&destination_path).await?;
|
||||
let first = dir.next().await.context("missing first file")??;
|
||||
let repo_root = destination_path.join("vscode-eslint");
|
||||
fs::rename(first.path(), &repo_root).await?;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
handle_symlink(
|
||||
repo_root.join("$shared"),
|
||||
repo_root.join("client").join("src").join("shared"),
|
||||
)
|
||||
.await?;
|
||||
handle_symlink(
|
||||
repo_root.join("$shared"),
|
||||
repo_root.join("server").join("src").join("shared"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.node
|
||||
.run_npm_subcommand(&repo_root, "install", &[])
|
||||
.await?;
|
||||
|
||||
self.node
|
||||
.run_npm_subcommand(&repo_root, "run-script", &["compile"])
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path =
|
||||
Self::build_destination_path(&container_dir).join(EsLintLspAdapter::SERVER_PATH);
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -2,13 +2,12 @@ use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use gpui::AsyncApp;
|
||||
use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
|
||||
use language::{LanguageName, LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||
use project::{Fs, lsp_store::language_server_settings};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -59,31 +58,29 @@ impl VtslsLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeScriptVersions {
|
||||
pub struct TypeScriptVersions {
|
||||
typescript_version: String,
|
||||
server_version: String,
|
||||
}
|
||||
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("vtsls");
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for VtslsLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
SERVER_NAME
|
||||
}
|
||||
impl LspInstaller for VtslsLspAdapter {
|
||||
type BinaryVersion = TypeScriptVersions;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(TypeScriptVersions {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<TypeScriptVersions> {
|
||||
Ok(TypeScriptVersions {
|
||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||
server_version: self
|
||||
.node
|
||||
.npm_package_latest_version("@vtsls/language-server")
|
||||
.await?,
|
||||
}) as Box<_>)
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -103,11 +100,10 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: TypeScriptVersions,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
|
||||
let mut packages_to_install = Vec::new();
|
||||
@@ -159,6 +155,13 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for VtslsLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
SERVER_NAME
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
Some(vec![
|
||||
|
||||
@@ -2,7 +2,9 @@ use anyhow::{Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::AsyncApp;
|
||||
use language::{LspAdapter, LspAdapterDelegate, Toolchain, language_settings::AllLanguageSettings};
|
||||
use language::{
|
||||
LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain, language_settings::AllLanguageSettings,
|
||||
};
|
||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||
use project::lsp_store::language_server_settings;
|
||||
@@ -10,7 +12,6 @@ use serde_json::Value;
|
||||
use settings::{Settings, SettingsLocation};
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -35,22 +36,18 @@ impl YamlLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for YamlLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
impl LspInstaller for YamlLspAdapter {
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("yaml-language-server")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version("yaml-language-server")
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
@@ -71,11 +68,10 @@ impl LspAdapter for YamlLspAdapter {
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
latest_version: Box<dyn 'static + Send + Any>,
|
||||
latest_version: String,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
self.node
|
||||
@@ -94,11 +90,10 @@ impl LspAdapter for YamlLspAdapter {
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &(dyn 'static + Send + Any),
|
||||
version: &String,
|
||||
container_dir: &PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let version = version.downcast_ref::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
@@ -129,6 +124,13 @@ impl LspAdapter for YamlLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for YamlLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME
|
||||
}
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
|
||||
@@ -62,7 +62,7 @@ pub enum IoKind {
|
||||
|
||||
/// Represents a launchable language server. This can either be a standalone binary or the path
|
||||
/// to a runtime with arguments to instruct it to launch the actual language server file.
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[derive(Clone)]
|
||||
pub struct LanguageServerBinary {
|
||||
pub path: PathBuf,
|
||||
pub arguments: Vec<OsString>,
|
||||
@@ -70,12 +70,14 @@ pub struct LanguageServerBinary {
|
||||
}
|
||||
|
||||
/// Configures the search (and installation) of language servers.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LanguageServerBinaryOptions {
|
||||
/// Whether the adapter should look at the users system
|
||||
pub allow_path_lookup: bool,
|
||||
/// Whether the adapter should download its own version
|
||||
pub allow_binary_download: bool,
|
||||
/// Whether the adapter should download a pre-release version
|
||||
pub pre_release: bool,
|
||||
}
|
||||
|
||||
/// A running language server process.
|
||||
|
||||
@@ -59,9 +59,9 @@ use itertools::Itertools as _;
|
||||
use language::{
|
||||
Bias, BinaryStatus, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic,
|
||||
DiagnosticEntry, DiagnosticSet, DiagnosticSourceKind, Diff, File as _, Language, LanguageName,
|
||||
LanguageRegistry, LocalFile, LspAdapter, LspAdapterDelegate, ManifestDelegate, ManifestName,
|
||||
Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPoint, ToPointUtf16, Toolchain, Transaction,
|
||||
Unclipped,
|
||||
LanguageRegistry, LocalFile, LspAdapter, LspAdapterDelegate, LspInstaller, ManifestDelegate,
|
||||
ManifestName, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Toolchain,
|
||||
Transaction, Unclipped,
|
||||
language_settings::{
|
||||
FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings,
|
||||
},
|
||||
@@ -96,7 +96,7 @@ use sha2::{Digest, Sha256};
|
||||
use smol::channel::Sender;
|
||||
use snippet::Snippet;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
cmp::{Ordering, Reverse},
|
||||
@@ -112,7 +112,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use sum_tree::Dimensions;
|
||||
use text::{Anchor, BufferId, LineEnding, OffsetRangeExt};
|
||||
use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, ToPoint as _};
|
||||
|
||||
use util::{
|
||||
ConnectionResult, ResultExt as _, debug_panic, defer, maybe, merge_json_value_into,
|
||||
@@ -534,6 +534,11 @@ impl LocalLspStore {
|
||||
.and_then(|b| b.ignore_system_version)
|
||||
.unwrap_or_default(),
|
||||
allow_binary_download,
|
||||
pre_release: settings
|
||||
.fetch
|
||||
.as_ref()
|
||||
.and_then(|f| f.pre_release)
|
||||
.unwrap_or(false),
|
||||
};
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
@@ -12579,27 +12584,8 @@ impl SshLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for SshLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
let Some(options) = &self.initialization_options else {
|
||||
return Ok(None);
|
||||
};
|
||||
let result = serde_json::from_str(options)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
self.code_action_kinds.clone()
|
||||
}
|
||||
|
||||
impl LspInstaller for SshLspAdapter {
|
||||
type BinaryVersion = ();
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
@@ -12620,14 +12606,15 @@ impl LspAdapter for SshLspAdapter {
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: &AsyncApp,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<()> {
|
||||
anyhow::bail!("SshLspAdapter does not support fetch_latest_server_version")
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: (),
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
@@ -12635,6 +12622,28 @@ impl LspAdapter for SshLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for SshLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
let Some(options) = &self.initialization_options else {
|
||||
return Ok(None);
|
||||
};
|
||||
let result = serde_json::from_str(options)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
self.code_action_kinds.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn language_server_settings<'a>(
|
||||
delegate: &'a dyn LspAdapterDelegate,
|
||||
language: &LanguageServerName,
|
||||
|
||||
Reference in New Issue
Block a user