Compare commits
2 commits
main
...
lw/lsp-ada
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9733d34c94 | ||
![]() |
aae59a2842 |
13 changed files with 731 additions and 734 deletions
|
@ -29,7 +29,7 @@ use async_trait::async_trait;
|
||||||
use collections::{HashMap, HashSet, IndexSet};
|
use collections::{HashMap, HashSet, IndexSet};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use gpui::{App, AsyncApp, Entity, SharedString, Task};
|
use gpui::{App, AsyncApp, Entity, SharedString};
|
||||||
pub use highlight_map::HighlightMap;
|
pub use highlight_map::HighlightMap;
|
||||||
use http_client::HttpClient;
|
use http_client::HttpClient;
|
||||||
pub use language_registry::{
|
pub use language_registry::{
|
||||||
|
@ -46,7 +46,6 @@ use settings::WorktreeId;
|
||||||
use smol::future::FutureExt as _;
|
use smol::future::FutureExt as _;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -306,127 +305,9 @@ pub trait LspAdapterDelegate: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait LspAdapter: 'static + Send + Sync {
|
pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller {
|
||||||
fn name(&self) -> LanguageServerName;
|
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,
|
|
||||||
) -> 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(
|
fn process_diagnostics(
|
||||||
&self,
|
&self,
|
||||||
_: &mut lsp::PublishDiagnosticsParams,
|
_: &mut lsp::PublishDiagnosticsParams,
|
||||||
|
@ -590,40 +471,180 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
|
pub trait LspInstaller {
|
||||||
adapter: &L,
|
type BinaryVersion;
|
||||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
fn check_if_user_installed(
|
||||||
container_dir: PathBuf,
|
&self,
|
||||||
cx: &mut AsyncApp,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
_: Option<Toolchain>,
|
||||||
if let Some(task) = adapter.will_fetch_server(delegate, cx) {
|
_: &AsyncApp,
|
||||||
task.await?;
|
) -> impl Future<Output = Option<LanguageServerBinary>> {
|
||||||
|
async { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = adapter.name();
|
fn fetch_latest_server_version(
|
||||||
log::debug!("fetching latest version of language server {:?}", name.0);
|
&self,
|
||||||
delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate);
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> impl Future<Output = Result<Self::BinaryVersion>>;
|
||||||
|
|
||||||
let latest_version = adapter
|
fn will_fetch_server(
|
||||||
.fetch_latest_server_version(delegate.as_ref())
|
&self,
|
||||||
.await?;
|
_: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
_: &mut AsyncApp,
|
||||||
|
) -> impl Future<Output = Result<()>> {
|
||||||
|
async { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(binary) = adapter
|
fn check_if_version_installed(
|
||||||
.check_if_version_installed(latest_version.as_ref(), &container_dir, delegate.as_ref())
|
&self,
|
||||||
.await
|
_version: &Self::BinaryVersion,
|
||||||
{
|
_container_dir: &PathBuf,
|
||||||
log::debug!("language server {:?} is already installed", name.0);
|
_delegate: &dyn LspAdapterDelegate,
|
||||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
) -> impl Future<Output = Option<LanguageServerBinary>> {
|
||||||
Ok(binary)
|
async { None }
|
||||||
} 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;
|
|
||||||
|
|
||||||
delegate.update_status(name.clone(), BinaryStatus::None);
|
fn fetch_server_binary(
|
||||||
binary
|
&self,
|
||||||
|
latest_version: Self::BinaryVersion,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> impl Future<Output = Result<LanguageServerBinary>>;
|
||||||
|
|
||||||
|
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,
|
||||||
|
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: futures::lock::MutexGuard<'a, Option<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,
|
||||||
|
cx: &mut AsyncApp,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
self.will_fetch_server(delegate, cx).await?;
|
||||||
|
|
||||||
|
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()).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,
|
||||||
|
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(), 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(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 = self.try_fetch_server_binary( &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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2176,10 +2197,14 @@ impl Default for FakeLspAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for FakeLspAdapter {
|
||||||
impl LspAdapter for FakeLspAdapter {
|
type BinaryVersion = ();
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
LanguageServerName(self.name.into())
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Self::BinaryVersion> {
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -2191,27 +2216,9 @@ impl LspAdapter for FakeLspAdapter {
|
||||||
Some(self.language_server_binary.clone())
|
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,
|
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
_: Box<dyn 'static + Send + Any>,
|
_: (),
|
||||||
_: PathBuf,
|
_: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
|
@ -2225,6 +2232,14 @@ impl LspAdapter for FakeLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
unreachable!();
|
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> {
|
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||||
self.disk_based_diagnostics_sources.clone()
|
self.disk_based_diagnostics_sources.clone()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::any::Any;
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
@ -12,8 +11,8 @@ use fs::Fs;
|
||||||
use futures::{Future, FutureExt, future::join_all};
|
use futures::{Future, FutureExt, future::join_all};
|
||||||
use gpui::{App, AppContext, AsyncApp, Task};
|
use gpui::{App, AppContext, AsyncApp, Task};
|
||||||
use language::{
|
use language::{
|
||||||
BinaryStatus, CodeLabel, HighlightId, Language, LanguageName, LspAdapter, LspAdapterDelegate,
|
BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,
|
||||||
Toolchain,
|
LspAdapterDelegate, Toolchain,
|
||||||
};
|
};
|
||||||
use lsp::{
|
use lsp::{
|
||||||
CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
|
CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
|
||||||
|
@ -151,11 +150,7 @@ impl ExtensionLspAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl LspAdapter for ExtensionLspAdapter {
|
impl DynLspInstaller for ExtensionLspAdapter {
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
self.language_server_id.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_language_server_command<'a>(
|
fn get_language_server_command<'a>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
|
@ -201,28 +196,20 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn try_fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &Arc<dyn LspAdapterDelegate>,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
|
||||||
unreachable!("get_language_server_command is overridden")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
|
||||||
&self,
|
|
||||||
_: Box<dyn 'static + Send + Any>,
|
|
||||||
_: PathBuf,
|
_: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &mut AsyncApp,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
unreachable!("get_language_server_command is overridden")
|
unreachable!("get_language_server_command is overriden")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn cached_server_binary(
|
#[async_trait(?Send)]
|
||||||
&self,
|
impl LspAdapter for ExtensionLspAdapter {
|
||||||
_: PathBuf,
|
fn name(&self) -> LanguageServerName {
|
||||||
_: &dyn LspAdapterDelegate,
|
self.language_server_id.clone()
|
||||||
) -> Option<LanguageServerBinary> {
|
|
||||||
unreachable!("get_language_server_command is overridden")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use lsp::{InitializeParams, LanguageServerBinary, LanguageServerName};
|
||||||
use project::lsp_store::clangd_ext;
|
use project::lsp_store::clangd_ext;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smol::fs;
|
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 util::{ResultExt, fs::remove_matching, maybe, merge_json_value_into};
|
||||||
|
|
||||||
use crate::github_download::{GithubBinaryMetadata, download_server_binary};
|
use crate::github_download::{GithubBinaryMetadata, download_server_binary};
|
||||||
|
@ -19,30 +19,13 @@ impl CLspAdapter {
|
||||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("clangd");
|
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("clangd");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for CLspAdapter {
|
||||||
impl super::LspAdapter for CLspAdapter {
|
type BinaryVersion = GitHubLspBinaryVersion;
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<GitHubLspBinaryVersion> {
|
||||||
let release =
|
let release =
|
||||||
latest_github_release("clangd/clangd", true, false, delegate.http_client()).await?;
|
latest_github_release("clangd/clangd", true, false, delegate.http_client()).await?;
|
||||||
let os_suffix = match consts::OS {
|
let os_suffix = match consts::OS {
|
||||||
|
@ -62,12 +45,26 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
url: asset.browser_download_url.clone(),
|
url: asset.browser_download_url.clone(),
|
||||||
digest: asset.digest.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(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
version: Box<dyn 'static + Send + Any>,
|
version: GitHubLspBinaryVersion,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
|
@ -75,7 +72,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
digest: expected_digest,
|
digest: expected_digest,
|
||||||
} = *version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
} = version;
|
||||||
let version_dir = container_dir.join(format!("clangd_{name}"));
|
let version_dir = container_dir.join(format!("clangd_{name}"));
|
||||||
let binary_path = version_dir.join("bin/clangd");
|
let binary_path = version_dir.join("bin/clangd");
|
||||||
|
|
||||||
|
@ -146,6 +143,13 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_server_binary(container_dir).await
|
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(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -2,14 +2,13 @@ use anyhow::{Context as _, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use language::{LspAdapter, LspAdapterDelegate, Toolchain};
|
use language::{LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
|
||||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
use project::{Fs, lsp_store::language_server_settings};
|
use project::{Fs, lsp_store::language_server_settings};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -34,10 +33,13 @@ impl CssLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for CssLspAdapter {
|
||||||
impl LspAdapter for CssLspAdapter {
|
type BinaryVersion = String;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
LanguageServerName("vscode-css-language-server".into())
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<String> {
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version("vscode-langservers-extracted")
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -58,24 +60,12 @@ impl LspAdapter for CssLspAdapter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> 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(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: String,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -94,11 +84,10 @@ impl LspAdapter for CssLspAdapter {
|
||||||
|
|
||||||
async fn check_if_version_installed(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &String,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -129,6 +118,13 @@ impl LspAdapter for CssLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_server_binary(container_dir, &self.node).await
|
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(
|
async fn initialization_options(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
|
|
@ -5,17 +5,17 @@ use futures::StreamExt;
|
||||||
use gpui::{App, AsyncApp, Task};
|
use gpui::{App, AsyncApp, Task};
|
||||||
use http_client::github::latest_github_release;
|
use http_client::github::latest_github_release;
|
||||||
pub use language::*;
|
pub use language::*;
|
||||||
|
use language::{LanguageToolchainStore, LspAdapterDelegate, LspInstaller};
|
||||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||||
use project::Fs;
|
use project::Fs;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ffi::{OsStr, OsString},
|
ffi::{OsStr, OsString},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
process::Output,
|
process::Output,
|
||||||
str,
|
str,
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -50,16 +50,13 @@ const BINARY: &str = if cfg!(target_os = "windows") {
|
||||||
"gopls"
|
"gopls"
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for GoLspAdapter {
|
||||||
impl super::LspAdapter for GoLspAdapter {
|
type BinaryVersion = Option<String>;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<Option<String>> {
|
||||||
let release =
|
let release =
|
||||||
latest_github_release("golang/tools", false, false, delegate.http_client()).await?;
|
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);
|
let version: Option<String> = release.tag_name.strip_prefix("gopls/v").map(str::to_string);
|
||||||
|
@ -69,7 +66,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
release.tag_name
|
release.tag_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(Box::new(version) as Box<_>)
|
Ok(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -86,36 +83,33 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn will_fetch_server(
|
async fn will_fetch_server(
|
||||||
&self,
|
&self,
|
||||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Result<()> {
|
||||||
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
const NOTIFICATION_MESSAGE: &str =
|
const NOTIFICATION_MESSAGE: &str =
|
||||||
"Could not install the Go language server `gopls`, because `go` was not found.";
|
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||||
|
|
||||||
let delegate = delegate.clone();
|
if delegate.which("go".as_ref()).await.is_none() {
|
||||||
Some(cx.spawn(async move |cx| {
|
if DID_SHOW_NOTIFICATION
|
||||||
if delegate.which("go".as_ref()).await.is_none() {
|
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||||
if DID_SHOW_NOTIFICATION
|
.is_ok()
|
||||||
.compare_exchange(false, true, SeqCst, SeqCst)
|
{
|
||||||
.is_ok()
|
cx.update(|cx| {
|
||||||
{
|
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||||
cx.update(|cx| {
|
})?
|
||||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
anyhow::bail!("cannot install gopls");
|
|
||||||
}
|
}
|
||||||
Ok(())
|
anyhow::bail!("cannot install gopls");
|
||||||
}))
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
version: Box<dyn 'static + Send + Any>,
|
version: Option<String>,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
|
@ -126,10 +120,8 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
.await
|
.await
|
||||||
.context("failed to get go version via `go version` command`")?;
|
.context("failed to get go version via `go version` command`")?;
|
||||||
let go_version = parse_version_output(&go_version_output)?;
|
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}"));
|
let binary_path = container_dir.join(format!("gopls_{version}_go_{go_version}"));
|
||||||
if let Ok(metadata) = fs::metadata(&binary_path).await
|
if let Ok(metadata) = fs::metadata(&binary_path).await
|
||||||
&& metadata.is_file()
|
&& metadata.is_file()
|
||||||
|
@ -145,10 +137,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
env: None,
|
env: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if let Some(path) = this
|
} else if let Some(path) = get_cached_server_binary(&container_dir).await {
|
||||||
.cached_server_binary(container_dir.clone(), delegate)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Ok(path);
|
return Ok(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +183,14 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> 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(
|
async fn initialization_options(
|
||||||
|
@ -442,10 +438,10 @@ fn parse_version_output(output: &Output) -> Result<&str> {
|
||||||
Ok(version)
|
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 {
|
maybe!(async {
|
||||||
let mut last_binary_path = None;
|
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 {
|
while let Some(entry) = entries.next().await {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
if entry.file_type().await?.is_file()
|
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 http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||||
use language::{
|
use language::{
|
||||||
ContextProvider, LanguageName, LanguageRegistry, LocalFile as _, LspAdapter,
|
ContextProvider, LanguageName, LanguageRegistry, LocalFile as _, LspAdapter,
|
||||||
LspAdapterDelegate, Toolchain,
|
LspAdapterDelegate, LspInstaller, Toolchain,
|
||||||
};
|
};
|
||||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
|
@ -22,7 +22,6 @@ use smol::{
|
||||||
lock::RwLock,
|
lock::RwLock,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
env::consts,
|
env::consts,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -294,10 +293,13 @@ fn generate_inspector_style_schema() -> serde_json_lenient::Value {
|
||||||
serde_json_lenient::to_value(schema).unwrap()
|
serde_json_lenient::to_value(schema).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for JsonLspAdapter {
|
||||||
impl LspAdapter for JsonLspAdapter {
|
type BinaryVersion = String;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
LanguageServerName("json-language-server".into())
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<String> {
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -318,24 +320,12 @@ impl LspAdapter for JsonLspAdapter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> 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(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &String,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -361,11 +351,10 @@ impl LspAdapter for JsonLspAdapter {
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: String,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -389,6 +378,13 @@ impl LspAdapter for JsonLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_server_binary(container_dir, &self.node).await
|
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(
|
async fn initialization_options(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
@ -485,16 +481,13 @@ impl NodeVersionAdapter {
|
||||||
LanguageServerName::new_static("package-version-server");
|
LanguageServerName::new_static("package-version-server");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for NodeVersionAdapter {
|
||||||
impl LspAdapter for NodeVersionAdapter {
|
type BinaryVersion = GitHubLspBinaryVersion;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<GitHubLspBinaryVersion> {
|
||||||
let release = latest_github_release(
|
let release = latest_github_release(
|
||||||
"zed-industries/package-version-server",
|
"zed-industries/package-version-server",
|
||||||
true,
|
true,
|
||||||
|
@ -519,11 +512,11 @@ impl LspAdapter for NodeVersionAdapter {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|asset| asset.name == asset_name)
|
.find(|asset| asset.name == asset_name)
|
||||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||||
Ok(Box::new(GitHubLspBinaryVersion {
|
Ok(GitHubLspBinaryVersion {
|
||||||
name: release.tag_name,
|
name: release.tag_name,
|
||||||
url: asset.browser_download_url.clone(),
|
url: asset.browser_download_url.clone(),
|
||||||
digest: asset.digest.clone(),
|
digest: asset.digest.clone(),
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -542,11 +535,11 @@ impl LspAdapter for NodeVersionAdapter {
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: GitHubLspBinaryVersion,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let version = latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
let version = &latest_version;
|
||||||
let destination_path = container_dir.join(format!(
|
let destination_path = container_dir.join(format!(
|
||||||
"{}-{}{}",
|
"{}-{}{}",
|
||||||
Self::SERVER_NAME,
|
Self::SERVER_NAME,
|
||||||
|
@ -596,6 +589,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> {
|
async fn get_cached_version_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
maybe!(async {
|
maybe!(async {
|
||||||
let mut last = None;
|
let mut last = None;
|
||||||
|
|
|
@ -9,7 +9,7 @@ use language::ToolchainList;
|
||||||
use language::ToolchainLister;
|
use language::ToolchainLister;
|
||||||
use language::language_settings::language_settings;
|
use language::language_settings::language_settings;
|
||||||
use language::{ContextLocation, LanguageToolchainStore};
|
use language::{ContextLocation, LanguageToolchainStore};
|
||||||
use language::{ContextProvider, LspAdapter, LspAdapterDelegate};
|
use language::{ContextProvider, LspAdapter, LspAdapterDelegate, LspInstaller};
|
||||||
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
|
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
|
||||||
use lsp::LanguageServerBinary;
|
use lsp::LanguageServerBinary;
|
||||||
use lsp::LanguageServerName;
|
use lsp::LanguageServerName;
|
||||||
|
@ -26,7 +26,6 @@ use std::cmp::Ordering;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
|
@ -100,28 +99,13 @@ impl PythonLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for PythonLspAdapter {
|
||||||
impl LspAdapter for PythonLspAdapter {
|
type BinaryVersion = String;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn initialization_options(
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<String> {
|
||||||
self: Arc<Self>,
|
self.node
|
||||||
_: &dyn Fs,
|
.npm_package_latest_version(Self::SERVER_NAME.as_ref())
|
||||||
_: &Arc<dyn LspAdapterDelegate>,
|
.await
|
||||||
) -> Result<Option<Value>> {
|
|
||||||
// Provide minimal initialization options
|
|
||||||
// Virtual environment configuration will be handled through workspace configuration
|
|
||||||
Ok(Some(json!({
|
|
||||||
"python": {
|
|
||||||
"analysis": {
|
|
||||||
"autoSearchPaths": true,
|
|
||||||
"useLibraryCodeForTypes": true,
|
|
||||||
"autoImportCompletions": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -155,24 +139,12 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> 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(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: String,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -192,11 +164,10 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
|
|
||||||
async fn check_if_version_installed(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &String,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -230,6 +201,31 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
binary.env = Some(delegate.shell_env().await);
|
binary.env = Some(delegate.shell_env().await);
|
||||||
Some(binary)
|
Some(binary)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl LspAdapter for PythonLspAdapter {
|
||||||
|
fn name(&self) -> LanguageServerName {
|
||||||
|
Self::SERVER_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_: &dyn Fs,
|
||||||
|
_: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
) -> Result<Option<Value>> {
|
||||||
|
// Provide minimal initialization options
|
||||||
|
// Virtual environment configuration will be handled through workspace configuration
|
||||||
|
Ok(Some(json!({
|
||||||
|
"python": {
|
||||||
|
"analysis": {
|
||||||
|
"autoSearchPaths": true,
|
||||||
|
"useLibraryCodeForTypes": true,
|
||||||
|
"autoImportCompletions": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||||
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||||
|
@ -1023,10 +1019,11 @@ const BINARY_DIR: &str = if cfg!(target_os = "windows") {
|
||||||
"bin"
|
"bin"
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for PyLspAdapter {
|
||||||
impl LspAdapter for PyLspAdapter {
|
type BinaryVersion = ();
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -1053,16 +1050,9 @@ impl LspAdapter for PyLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
|
||||||
Ok(Box::new(()) as Box<_>)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
_: Box<dyn 'static + Send + Any>,
|
_: (),
|
||||||
_: PathBuf,
|
_: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
|
@ -1122,6 +1112,13 @@ impl LspAdapter for PyLspAdapter {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl LspAdapter for PyLspAdapter {
|
||||||
|
fn name(&self) -> LanguageServerName {
|
||||||
|
Self::SERVER_NAME
|
||||||
|
}
|
||||||
|
|
||||||
async fn process_completions(&self, _items: &mut [lsp::CompletionItem]) {}
|
async fn process_completions(&self, _items: &mut [lsp::CompletionItem]) {}
|
||||||
|
|
||||||
|
@ -1315,28 +1312,11 @@ impl BasedPyrightLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for BasedPyrightLspAdapter {
|
||||||
impl LspAdapter for BasedPyrightLspAdapter {
|
type BinaryVersion = ();
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn initialization_options(
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<()> {
|
||||||
self: Arc<Self>,
|
Ok(())
|
||||||
_: &dyn Fs,
|
|
||||||
_: &Arc<dyn LspAdapterDelegate>,
|
|
||||||
) -> Result<Option<Value>> {
|
|
||||||
// Provide minimal initialization options
|
|
||||||
// Virtual environment configuration will be handled through workspace configuration
|
|
||||||
Ok(Some(json!({
|
|
||||||
"python": {
|
|
||||||
"analysis": {
|
|
||||||
"autoSearchPaths": true,
|
|
||||||
"useLibraryCodeForTypes": true,
|
|
||||||
"autoImportCompletions": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -1364,16 +1344,9 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
|
||||||
Ok(Box::new(()) as Box<_>)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
_latest_version: Box<dyn 'static + Send + Any>,
|
_latest_version: (),
|
||||||
_container_dir: PathBuf,
|
_container_dir: PathBuf,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
|
@ -1411,6 +1384,31 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||||
arguments: vec!["--stdio".into()],
|
arguments: vec!["--stdio".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl LspAdapter for BasedPyrightLspAdapter {
|
||||||
|
fn name(&self) -> LanguageServerName {
|
||||||
|
Self::SERVER_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_: &dyn Fs,
|
||||||
|
_: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
) -> Result<Option<Value>> {
|
||||||
|
// Provide minimal initialization options
|
||||||
|
// Virtual environment configuration will be handled through workspace configuration
|
||||||
|
Ok(Some(json!({
|
||||||
|
"python": {
|
||||||
|
"analysis": {
|
||||||
|
"autoSearchPaths": true,
|
||||||
|
"useLibraryCodeForTypes": true,
|
||||||
|
"autoImportCompletions": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||||
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||||
|
|
|
@ -17,7 +17,6 @@ use smol::fs::{self};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, LazyLock},
|
sync::{Arc, LazyLock},
|
||||||
|
@ -109,156 +108,6 @@ impl LspAdapter for RustLspAdapter {
|
||||||
SERVER_NAME
|
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,
|
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
|
||||||
let release = latest_github_release(
|
|
||||||
"rust-lang/rust-analyzer",
|
|
||||||
true,
|
|
||||||
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> {
|
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||||
vec![CARGO_DIAGNOSTICS_SOURCE_NAME.to_owned()]
|
vec![CARGO_DIAGNOSTICS_SOURCE_NAME.to_owned()]
|
||||||
}
|
}
|
||||||
|
@ -528,6 +377,159 @@ 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,
|
||||||
|
) -> Result<GitHubLspBinaryVersion> {
|
||||||
|
let release = latest_github_release(
|
||||||
|
"rust-lang/rust-analyzer",
|
||||||
|
true,
|
||||||
|
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(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;
|
pub(crate) struct RustContextProvider;
|
||||||
|
|
||||||
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
||||||
|
|
|
@ -3,14 +3,13 @@ use async_trait::async_trait;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
|
use language::{LanguageName, LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
|
||||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
use project::{Fs, lsp_store::language_server_settings};
|
use project::{Fs, lsp_store::language_server_settings};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -41,10 +40,13 @@ impl TailwindLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for TailwindLspAdapter {
|
||||||
impl LspAdapter for TailwindLspAdapter {
|
type BinaryVersion = String;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<String> {
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version(Self::PACKAGE_NAME)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -63,24 +65,12 @@ impl LspAdapter for TailwindLspAdapter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> 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(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: String,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -99,11 +89,10 @@ impl LspAdapter for TailwindLspAdapter {
|
||||||
|
|
||||||
async fn check_if_version_installed(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &String,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -134,6 +123,13 @@ impl LspAdapter for TailwindLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_server_binary(container_dir, &self.node).await
|
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(
|
async fn initialization_options(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use gpui::{App, AppContext, AsyncApp, Task};
|
||||||
use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
|
use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
|
||||||
use language::{
|
use language::{
|
||||||
ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter,
|
ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter,
|
||||||
LspAdapterDelegate, Toolchain,
|
LspAdapterDelegate, LspInstaller, Toolchain,
|
||||||
};
|
};
|
||||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
|
@ -15,7 +15,6 @@ use project::{Fs, lsp_store::language_server_settings};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use smol::{fs, lock::RwLock, stream::StreamExt};
|
use smol::{fs, lock::RwLock, stream::StreamExt};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -549,37 +548,33 @@ impl TypeScriptLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeScriptVersions {
|
pub struct TypeScriptVersions {
|
||||||
typescript_version: String,
|
typescript_version: String,
|
||||||
server_version: String,
|
server_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for TypeScriptLspAdapter {
|
||||||
impl LspAdapter for TypeScriptLspAdapter {
|
type BinaryVersion = TypeScriptVersions;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<TypeScriptVersions> {
|
||||||
Ok(Box::new(TypeScriptVersions {
|
Ok(TypeScriptVersions {
|
||||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||||
server_version: self
|
server_version: self
|
||||||
.node
|
.node
|
||||||
.npm_package_latest_version("typescript-language-server")
|
.npm_package_latest_version("typescript-language-server")
|
||||||
.await?,
|
.await?,
|
||||||
}) as Box<_>)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_version_installed(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &TypeScriptVersions,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<TypeScriptVersions>().unwrap();
|
|
||||||
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -605,11 +600,10 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: TypeScriptVersions,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
|
||||||
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -642,6 +636,13 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
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>> {
|
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
|
@ -809,6 +810,97 @@ impl EsLintLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LspInstaller for EsLintLspAdapter {
|
||||||
|
type BinaryVersion = GitHubLspBinaryVersion;
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> 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)]
|
#[async_trait(?Send)]
|
||||||
impl LspAdapter for EsLintLspAdapter {
|
impl LspAdapter for EsLintLspAdapter {
|
||||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||||
|
@ -881,94 +973,6 @@ impl LspAdapter for EsLintLspAdapter {
|
||||||
fn name(&self) -> LanguageServerName {
|
fn name(&self) -> LanguageServerName {
|
||||||
Self::SERVER_NAME
|
Self::SERVER_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_delegate: &dyn LspAdapterDelegate,
|
|
||||||
) -> 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")]
|
#[cfg(target_os = "windows")]
|
||||||
|
|
|
@ -2,13 +2,12 @@ use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
|
use language::{LanguageName, LspAdapter, LspAdapterDelegate, LspInstaller, Toolchain};
|
||||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
use project::{Fs, lsp_store::language_server_settings};
|
use project::{Fs, lsp_store::language_server_settings};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -57,30 +56,27 @@ impl VtslsLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeScriptVersions {
|
pub struct TypeScriptVersions {
|
||||||
typescript_version: String,
|
typescript_version: String,
|
||||||
server_version: String,
|
server_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("vtsls");
|
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("vtsls");
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for VtslsLspAdapter {
|
||||||
impl LspAdapter for VtslsLspAdapter {
|
type BinaryVersion = TypeScriptVersions;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<TypeScriptVersions> {
|
||||||
Ok(Box::new(TypeScriptVersions {
|
Ok(TypeScriptVersions {
|
||||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||||
server_version: self
|
server_version: self
|
||||||
.node
|
.node
|
||||||
.npm_package_latest_version("@vtsls/language-server")
|
.npm_package_latest_version("@vtsls/language-server")
|
||||||
.await?,
|
.await?,
|
||||||
}) as Box<_>)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -100,11 +96,10 @@ impl LspAdapter for VtslsLspAdapter {
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: TypeScriptVersions,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<TypeScriptVersions>().unwrap();
|
|
||||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||||
|
|
||||||
let mut packages_to_install = Vec::new();
|
let mut packages_to_install = Vec::new();
|
||||||
|
@ -156,6 +151,13 @@ impl LspAdapter for VtslsLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
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>> {
|
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
|
|
|
@ -2,7 +2,9 @@ use anyhow::{Context as _, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::AsyncApp;
|
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 lsp::{LanguageServerBinary, LanguageServerName};
|
||||||
use node_runtime::{NodeRuntime, VersionStrategy};
|
use node_runtime::{NodeRuntime, VersionStrategy};
|
||||||
use project::{Fs, lsp_store::language_server_settings};
|
use project::{Fs, lsp_store::language_server_settings};
|
||||||
|
@ -10,7 +12,6 @@ use serde_json::Value;
|
||||||
use settings::{Settings, SettingsLocation};
|
use settings::{Settings, SettingsLocation};
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -35,21 +36,13 @@ impl YamlLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
impl LspInstaller for YamlLspAdapter {
|
||||||
impl LspAdapter for YamlLspAdapter {
|
type BinaryVersion = String;
|
||||||
fn name(&self) -> LanguageServerName {
|
|
||||||
Self::SERVER_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<String> {
|
||||||
&self,
|
self.node
|
||||||
_: &dyn LspAdapterDelegate,
|
.npm_package_latest_version("yaml-language-server")
|
||||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
.await
|
||||||
Ok(Box::new(
|
|
||||||
self.node
|
|
||||||
.npm_package_latest_version("yaml-language-server")
|
|
||||||
.await?,
|
|
||||||
) as Box<_>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
async fn check_if_user_installed(
|
||||||
|
@ -70,11 +63,10 @@ impl LspAdapter for YamlLspAdapter {
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
latest_version: Box<dyn 'static + Send + Any>,
|
latest_version: String,
|
||||||
container_dir: PathBuf,
|
container_dir: PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let latest_version = latest_version.downcast::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
self.node
|
self.node
|
||||||
|
@ -93,11 +85,10 @@ impl LspAdapter for YamlLspAdapter {
|
||||||
|
|
||||||
async fn check_if_version_installed(
|
async fn check_if_version_installed(
|
||||||
&self,
|
&self,
|
||||||
version: &(dyn 'static + Send + Any),
|
version: &String,
|
||||||
container_dir: &PathBuf,
|
container_dir: &PathBuf,
|
||||||
_: &dyn LspAdapterDelegate,
|
_: &dyn LspAdapterDelegate,
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
let version = version.downcast_ref::<String>().unwrap();
|
|
||||||
let server_path = container_dir.join(SERVER_PATH);
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
let should_install_language_server = self
|
let should_install_language_server = self
|
||||||
|
@ -128,6 +119,13 @@ impl LspAdapter for YamlLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
get_cached_server_binary(container_dir, &self.node).await
|
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(
|
async fn workspace_configuration(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
|
|
@ -55,9 +55,9 @@ use itertools::Itertools as _;
|
||||||
use language::{
|
use language::{
|
||||||
Bias, BinaryStatus, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic,
|
Bias, BinaryStatus, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic,
|
||||||
DiagnosticEntry, DiagnosticSet, DiagnosticSourceKind, Diff, File as _, Language, LanguageName,
|
DiagnosticEntry, DiagnosticSet, DiagnosticSourceKind, Diff, File as _, Language, LanguageName,
|
||||||
LanguageRegistry, LocalFile, LspAdapter, LspAdapterDelegate, ManifestDelegate, ManifestName,
|
LanguageRegistry, LocalFile, LspAdapter, LspAdapterDelegate, LspInstaller, ManifestDelegate,
|
||||||
Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Toolchain, Transaction,
|
ManifestName, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Toolchain,
|
||||||
Unclipped,
|
Transaction, Unclipped,
|
||||||
language_settings::{
|
language_settings::{
|
||||||
FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings,
|
FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings,
|
||||||
},
|
},
|
||||||
|
@ -92,7 +92,6 @@ use sha2::{Digest, Sha256};
|
||||||
use smol::channel::Sender;
|
use smol::channel::Sender;
|
||||||
use snippet::Snippet;
|
use snippet::Snippet;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
cmp::{Ordering, Reverse},
|
cmp::{Ordering, Reverse},
|
||||||
|
@ -12773,6 +12772,39 @@ impl SshLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LspInstaller for SshLspAdapter {
|
||||||
|
type BinaryVersion = ();
|
||||||
|
async fn check_if_user_installed(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
_: Option<Toolchain>,
|
||||||
|
_: &AsyncApp,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
Some(self.binary.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
_: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(&self, _: &dyn LspAdapterDelegate) -> Result<()> {
|
||||||
|
anyhow::bail!("SshLspAdapter does not support fetch_latest_server_version")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
_: (),
|
||||||
|
_: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
anyhow::bail!("SshLspAdapter does not support fetch_server_binary")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl LspAdapter for SshLspAdapter {
|
impl LspAdapter for SshLspAdapter {
|
||||||
fn name(&self) -> LanguageServerName {
|
fn name(&self) -> LanguageServerName {
|
||||||
|
@ -12794,39 +12826,6 @@ impl LspAdapter for SshLspAdapter {
|
||||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||||
self.code_action_kinds.clone()
|
self.code_action_kinds.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_if_user_installed(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
_: Option<Toolchain>,
|
|
||||||
_: &AsyncApp,
|
|
||||||
) -> Option<LanguageServerBinary> {
|
|
||||||
Some(self.binary.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn cached_server_binary(
|
|
||||||
&self,
|
|
||||||
_: PathBuf,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> Option<LanguageServerBinary> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
|
||||||
&self,
|
|
||||||
_: &dyn LspAdapterDelegate,
|
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
|
||||||
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> {
|
|
||||||
anyhow::bail!("SshLspAdapter does not support fetch_server_binary")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language_server_settings<'a>(
|
pub fn language_server_settings<'a>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue