mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-26 20:22:30 +00:00
Initial impl of NodeRuntime
w/JSON borked and a deadlock :)
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
1a2e509e35
commit
c72d33e029
20 changed files with 435 additions and 375 deletions
|
@ -29,6 +29,7 @@ use std::{
|
|||
any::Any,
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
ffi::OsString,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
mem,
|
||||
|
@ -86,7 +87,7 @@ pub enum ServerExecutionKind {
|
|||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LanguageServerBinary {
|
||||
pub path: PathBuf,
|
||||
pub arguments: Vec<String>,
|
||||
pub arguments: Vec<OsString>,
|
||||
}
|
||||
|
||||
/// Represents a Language Server, with certain cached sync properties.
|
||||
|
@ -184,8 +185,6 @@ impl CachedLspAdapter {
|
|||
pub trait LspAdapter: 'static + Send + Sync {
|
||||
async fn name(&self) -> LanguageServerName;
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
@ -494,7 +493,6 @@ pub struct LanguageRegistry {
|
|||
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
login_shell_env_loaded: Shared<Task<()>>,
|
||||
node_path: Shared<Task<Option<PathBuf>>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
lsp_binary_paths: Mutex<
|
||||
HashMap<
|
||||
|
@ -516,7 +514,7 @@ struct LanguageRegistryState {
|
|||
}
|
||||
|
||||
impl LanguageRegistry {
|
||||
pub fn new(login_shell_env_loaded: Task<()>, node_path: Task<Option<PathBuf>>) -> Self {
|
||||
pub fn new(login_shell_env_loaded: Task<()>) -> Self {
|
||||
let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16);
|
||||
Self {
|
||||
state: RwLock::new(LanguageRegistryState {
|
||||
|
@ -532,7 +530,6 @@ impl LanguageRegistry {
|
|||
lsp_binary_statuses_tx,
|
||||
lsp_binary_statuses_rx,
|
||||
login_shell_env_loaded: login_shell_env_loaded.shared(),
|
||||
node_path: node_path.shared(),
|
||||
lsp_binary_paths: Default::default(),
|
||||
executor: None,
|
||||
}
|
||||
|
@ -540,7 +537,7 @@ impl LanguageRegistry {
|
|||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test() -> Self {
|
||||
Self::new(Task::ready(()), Task::Ready(None))
|
||||
Self::new(Task::ready(()))
|
||||
}
|
||||
|
||||
pub fn set_executor(&mut self, executor: Arc<Background>) {
|
||||
|
@ -795,6 +792,8 @@ impl LanguageRegistry {
|
|||
Ok(server)
|
||||
}));
|
||||
}
|
||||
|
||||
dbg!();
|
||||
|
||||
let download_dir = self
|
||||
.language_server_download_dir
|
||||
|
@ -806,18 +805,18 @@ impl LanguageRegistry {
|
|||
let adapter = language.adapter.clone()?;
|
||||
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
|
||||
let login_shell_env_loaded = self.login_shell_env_loaded.clone();
|
||||
let node_path = self.node_path.clone();
|
||||
dbg!();
|
||||
|
||||
Some(cx.spawn(|cx| async move {
|
||||
login_shell_env_loaded.await;
|
||||
let node_path = node_path.await;
|
||||
|
||||
let server_binary = this
|
||||
let binary = this
|
||||
.lsp_binary_paths
|
||||
.lock()
|
||||
.entry(adapter.name.clone())
|
||||
.or_insert_with(|| {
|
||||
get_server_binary(
|
||||
dbg!();
|
||||
get_binary(
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
http_client,
|
||||
|
@ -829,30 +828,19 @@ impl LanguageRegistry {
|
|||
.shared()
|
||||
})
|
||||
.clone()
|
||||
.map_err(|e| anyhow!(e));
|
||||
.map_err(|e| anyhow!(e))
|
||||
.await?;
|
||||
dbg!();
|
||||
|
||||
let server_binary = server_binary.await?;
|
||||
let server_name = server_binary
|
||||
.path
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string());
|
||||
|
||||
let mut command = match adapter.adapter.server_execution_kind().await {
|
||||
ServerExecutionKind::Node => {
|
||||
let node_path = node_path
|
||||
.ok_or(anyhow!("Missing Node path for Node based language server"))?;
|
||||
let node_binary = node_path.join("bin/node");
|
||||
let mut command = smol::process::Command::new(node_binary);
|
||||
command.arg(dbg!(server_binary.path));
|
||||
command
|
||||
}
|
||||
|
||||
ServerExecutionKind::Launch => smol::process::Command::new(server_binary.path),
|
||||
};
|
||||
|
||||
command.args(dbg!(&server_binary.arguments));
|
||||
let server = lsp::LanguageServer::new(server_id, server_name, command, &root_path, cx)?;
|
||||
let server = lsp::LanguageServer::new(
|
||||
server_id,
|
||||
&binary.path,
|
||||
&binary.arguments,
|
||||
&root_path,
|
||||
cx,
|
||||
)?;
|
||||
|
||||
dbg!();
|
||||
Ok(server)
|
||||
}))
|
||||
}
|
||||
|
@ -882,7 +870,7 @@ impl Default for LanguageRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_server_binary(
|
||||
async fn get_binary(
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: Arc<Language>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
|
@ -896,7 +884,7 @@ async fn get_server_binary(
|
|||
.context("failed to create container directory")?;
|
||||
}
|
||||
|
||||
let binary = fetch_latest_server_binary(
|
||||
let binary = fetch_latest_binary(
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
http_client,
|
||||
|
@ -924,7 +912,7 @@ async fn get_server_binary(
|
|||
binary
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_binary(
|
||||
async fn fetch_latest_binary(
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: Arc<Language>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
|
@ -1471,10 +1459,6 @@ impl LspAdapter for Arc<FakeLspAdapter> {
|
|||
LanguageServerName(self.name.into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -105,10 +105,10 @@ struct Error {
|
|||
}
|
||||
|
||||
impl LanguageServer {
|
||||
pub fn new(
|
||||
pub fn new<T: AsRef<std::ffi::OsStr>>(
|
||||
server_id: usize,
|
||||
server_name: Option<String>,
|
||||
mut command: process::Command,
|
||||
binary_path: &Path,
|
||||
arguments: &[T],
|
||||
root_path: &Path,
|
||||
cx: AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
|
@ -118,13 +118,14 @@ impl LanguageServer {
|
|||
root_path.parent().unwrap_or_else(|| Path::new("/"))
|
||||
};
|
||||
|
||||
let mut server = dbg!(command
|
||||
let mut server = process::Command::new(binary_path)
|
||||
.current_dir(working_dir)
|
||||
.args(arguments)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.kill_on_drop(true))
|
||||
.spawn()?;
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
|
||||
let stdin = server.stdin.take().unwrap();
|
||||
let stout = server.stdout.take().unwrap();
|
||||
|
@ -147,8 +148,8 @@ impl LanguageServer {
|
|||
},
|
||||
);
|
||||
|
||||
if let Some(name) = server_name {
|
||||
server.name = name;
|
||||
if let Some(name) = binary_path.file_name() {
|
||||
server.name = name.to_string_lossy().to_string();
|
||||
}
|
||||
Ok(server)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use anyhow::Context;
|
||||
use client::http::HttpClient;
|
||||
use gpui::executor::Background;
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use theme::ThemeRegistry;
|
||||
|
@ -12,14 +15,13 @@ mod installation;
|
|||
mod json;
|
||||
mod language_plugin;
|
||||
mod lua;
|
||||
mod node_runtime;
|
||||
mod python;
|
||||
mod ruby;
|
||||
mod rust;
|
||||
mod typescript;
|
||||
mod yaml;
|
||||
|
||||
pub use installation::ensure_node_installation_dir;
|
||||
|
||||
// 1. Add tree-sitter-{language} parser to zed crate
|
||||
// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
|
||||
// 3. Add config.toml to the newly created language directory using existing languages as a template
|
||||
|
@ -34,7 +36,14 @@ pub use installation::ensure_node_installation_dir;
|
|||
#[exclude = "*.rs"]
|
||||
struct LanguageDir;
|
||||
|
||||
pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
||||
pub fn init(
|
||||
http: Arc<dyn HttpClient>,
|
||||
background: Arc<Background>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
) {
|
||||
let node_runtime = NodeRuntime::new(http, background);
|
||||
|
||||
for (name, grammar, lsp_adapter) in [
|
||||
(
|
||||
"c",
|
||||
|
@ -65,6 +74,7 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
"json",
|
||||
tree_sitter_json::language(),
|
||||
Some(Arc::new(json::JsonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
languages.clone(),
|
||||
themes.clone(),
|
||||
))),
|
||||
|
@ -77,7 +87,9 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"python",
|
||||
tree_sitter_python::language(),
|
||||
Some(Arc::new(python::PythonLspAdapter)),
|
||||
Some(Arc::new(python::PythonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))),
|
||||
),
|
||||
(
|
||||
"rust",
|
||||
|
@ -92,22 +104,28 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"tsx",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))),
|
||||
),
|
||||
(
|
||||
"typescript",
|
||||
tree_sitter_typescript::language_typescript(),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))),
|
||||
),
|
||||
(
|
||||
"javascript",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))),
|
||||
),
|
||||
(
|
||||
"html",
|
||||
tree_sitter_html::language(),
|
||||
Some(Arc::new(html::HtmlLspAdapter)),
|
||||
Some(Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))),
|
||||
),
|
||||
(
|
||||
"ruby",
|
||||
|
@ -137,7 +155,7 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"yaml",
|
||||
tree_sitter_yaml::language(),
|
||||
Some(Arc::new(yaml::YamlLspAdapter)),
|
||||
Some(Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))),
|
||||
),
|
||||
] {
|
||||
languages.register(name, load_config(name), grammar, lsp_adapter, load_queries);
|
||||
|
|
|
@ -16,10 +16,6 @@ impl super::LspAdapter for CLspAdapter {
|
|||
LanguageServerName("clangd".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -17,10 +17,6 @@ impl LspAdapter for ElixirLspAdapter {
|
|||
LanguageServerName("elixir-ls".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -7,10 +7,10 @@ pub use language::*;
|
|||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use smol::{fs, process};
|
||||
use std::{any::Any, ops::Range, path::PathBuf, str, sync::Arc};
|
||||
use std::{any::Any, ffi::OsString, ops::Range, path::PathBuf, str, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
fn server_binary_arguments() -> Vec<OsString> {
|
||||
vec!["-mode=stdio".into()]
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,6 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
LanguageServerName("gopls".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -1,23 +1,34 @@
|
|||
use super::installation::{npm_install_packages, npm_package_latest_version};
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
vec!["--stdio".into()]
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
pub struct HtmlLspAdapter;
|
||||
pub struct HtmlLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
}
|
||||
|
||||
impl HtmlLspAdapter {
|
||||
const BIN_PATH: &'static str =
|
||||
const SERVER_PATH: &'static str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-html-language-server";
|
||||
|
||||
pub fn new(node: Arc<NodeRuntime>) -> Self {
|
||||
HtmlLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -26,24 +37,21 @@ impl LspAdapter for HtmlLspAdapter {
|
|||
LanguageServerName("vscode-html-language-server".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Node
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(
|
||||
Box::new(npm_package_latest_version(http, "vscode-langservers-extracted").await?)
|
||||
as Box<_>,
|
||||
)
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<String>().unwrap();
|
||||
|
@ -51,15 +59,15 @@ impl LspAdapter for HtmlLspAdapter {
|
|||
fs::create_dir_all(&version_dir)
|
||||
.await
|
||||
.context("failed to create version directory")?;
|
||||
let binary_path = version_dir.join(Self::BIN_PATH);
|
||||
let server_path = version_dir.join(Self::SERVER_PATH);
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
npm_install_packages(
|
||||
http,
|
||||
[("vscode-langservers-extracted", version.as_str())],
|
||||
&version_dir,
|
||||
)
|
||||
.await?;
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
[("vscode-langservers-extracted", version.as_str())],
|
||||
&version_dir,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
|
||||
while let Some(entry) = entries.next().await {
|
||||
|
@ -74,8 +82,8 @@ impl LspAdapter for HtmlLspAdapter {
|
|||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -90,11 +98,11 @@ impl LspAdapter for HtmlLspAdapter {
|
|||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let bin_path = last_version_dir.join(Self::BIN_PATH);
|
||||
if bin_path.exists() {
|
||||
let server_path = last_version_dir.join(Self::SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: bin_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
|
|
|
@ -1,34 +1,14 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use anyhow::{Context, Result};
|
||||
use client::http::HttpClient;
|
||||
use futures::io::BufReader;
|
||||
use serde::Deserialize;
|
||||
use smol::fs::{self};
|
||||
use smol::io::AsyncReadExt;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct GitHubLspBinaryVersion {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct NpmInfo {
|
||||
#[serde(default)]
|
||||
dist_tags: NpmInfoDistTags,
|
||||
versions: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
struct NpmInfoDistTags {
|
||||
latest: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct GithubRelease {
|
||||
pub name: String,
|
||||
|
@ -41,101 +21,6 @@ pub(crate) struct GithubReleaseAsset {
|
|||
pub browser_download_url: String,
|
||||
}
|
||||
|
||||
pub async fn ensure_node_installation_dir(http: Arc<dyn HttpClient>) -> Result<PathBuf> {
|
||||
eprintln!("ensure_node_installation_dir");
|
||||
|
||||
let version = "v18.15.0";
|
||||
let arch = "arm64";
|
||||
|
||||
let folder_name = format!("node-{version}-darwin-{arch}");
|
||||
let node_containing_dir = util::paths::SUPPORT_DIR.join("node");
|
||||
let node_dir = node_containing_dir.join(folder_name);
|
||||
let node_binary = node_dir.join("bin/node");
|
||||
|
||||
if fs::metadata(&node_binary).await.is_err() {
|
||||
_ = fs::remove_dir_all(&node_containing_dir).await;
|
||||
fs::create_dir(&node_containing_dir)
|
||||
.await
|
||||
.context("error creating node containing dir")?;
|
||||
|
||||
let url = format!("https://nodejs.org/dist/{version}/node-{version}-darwin-{arch}.tar.gz");
|
||||
let mut response = http
|
||||
.get(&url, Default::default(), true)
|
||||
.await
|
||||
.context("error downloading Node binary tarball")?;
|
||||
|
||||
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
|
||||
let archive = Archive::new(decompressed_bytes);
|
||||
archive.unpack(&node_containing_dir).await?;
|
||||
eprintln!("unpacked");
|
||||
}
|
||||
|
||||
Ok(node_dir)
|
||||
}
|
||||
|
||||
pub async fn npm_package_latest_version(http: Arc<dyn HttpClient>, name: &str) -> Result<String> {
|
||||
let node_dir = ensure_node_installation_dir(http).await?;
|
||||
let node_binary = node_dir.join("bin/npm");
|
||||
let npm_file = node_dir.join("bin/npm");
|
||||
|
||||
let output = smol::process::Command::new(node_binary)
|
||||
.arg(npm_file)
|
||||
.args(["-fetch-retry-mintimeout", "2000"])
|
||||
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||
.args(["-fetch-timeout", "5000"])
|
||||
.args(["info", name, "--json"])
|
||||
.output()
|
||||
.await
|
||||
.context("failed to run npm info")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!(
|
||||
"failed to execute npm info:\nstdout: {:?}\nstderr: {:?}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))?;
|
||||
}
|
||||
let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
|
||||
info.dist_tags
|
||||
.latest
|
||||
.or_else(|| info.versions.pop())
|
||||
.ok_or_else(|| anyhow!("no version found for npm package {}", name))
|
||||
}
|
||||
|
||||
pub async fn npm_install_packages(
|
||||
http: Arc<dyn HttpClient>,
|
||||
packages: impl IntoIterator<Item = (&str, &str)>,
|
||||
directory: &Path,
|
||||
) -> Result<()> {
|
||||
let node_dir = ensure_node_installation_dir(http).await?;
|
||||
let node_binary = node_dir.join("bin/npm");
|
||||
let npm_file = node_dir.join("bin/npm");
|
||||
|
||||
let output = smol::process::Command::new(node_binary)
|
||||
.arg(npm_file)
|
||||
.args(["-fetch-retry-mintimeout", "2000"])
|
||||
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||
.args(["-fetch-timeout", "5000"])
|
||||
.arg("install")
|
||||
.arg("--prefix")
|
||||
.arg(directory)
|
||||
.args(
|
||||
packages
|
||||
.into_iter()
|
||||
.map(|(name, version)| format!("{name}@{version}")),
|
||||
)
|
||||
.output()
|
||||
.await
|
||||
.context("failed to run npm install")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!(
|
||||
"failed to execute npm install:\nstdout: {:?}\nstderr: {:?}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn latest_github_release(
|
||||
repo_name_with_owner: &str,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use super::installation::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use super::{
|
||||
installation::{latest_github_release, GitHubLspBinaryVersion},
|
||||
node_runtime::NodeRuntime,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_trait::async_trait;
|
||||
|
@ -6,15 +9,14 @@ use client::http::HttpClient;
|
|||
use collections::HashMap;
|
||||
use futures::{future::BoxFuture, io::BufReader, FutureExt, StreamExt};
|
||||
use gpui::MutableAppContext;
|
||||
use language::{
|
||||
LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind,
|
||||
};
|
||||
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::json;
|
||||
use settings::{keymap_file_json_schema, settings_file_json_schema};
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
any::Any,
|
||||
env::consts,
|
||||
ffi::OsString,
|
||||
future,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
|
@ -22,18 +24,27 @@ use std::{
|
|||
use theme::ThemeRegistry;
|
||||
use util::{paths, ResultExt, StaffMode};
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
vec!["--stdio".into()]
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
dbg!(vec![server_path.into(), "--stdio".into()])
|
||||
}
|
||||
|
||||
pub struct JsonLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
}
|
||||
|
||||
impl JsonLspAdapter {
|
||||
pub fn new(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) -> Self {
|
||||
Self { languages, themes }
|
||||
pub fn new(
|
||||
node: Arc<NodeRuntime>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
) -> Self {
|
||||
JsonLspAdapter {
|
||||
node,
|
||||
languages,
|
||||
themes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,10 +54,6 @@ impl LspAdapter for JsonLspAdapter {
|
|||
LanguageServerName("json-language-server".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Node
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
@ -105,8 +112,8 @@ impl LspAdapter for JsonLspAdapter {
|
|||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: destination_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&destination_path),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -118,8 +125,10 @@ impl LspAdapter for JsonLspAdapter {
|
|||
last = Some(entry?.path());
|
||||
}
|
||||
anyhow::Ok(LanguageServerBinary {
|
||||
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(
|
||||
&last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||
),
|
||||
})
|
||||
})()
|
||||
.await
|
||||
|
|
|
@ -4,7 +4,7 @@ use client::http::HttpClient;
|
|||
use collections::HashMap;
|
||||
use futures::lock::Mutex;
|
||||
use gpui::executor::Background;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
@ -32,7 +32,6 @@ pub async fn new_json(executor: Arc<Background>) -> Result<PluginLspAdapter> {
|
|||
|
||||
pub struct PluginLspAdapter {
|
||||
name: WasiFn<(), String>,
|
||||
server_execution_kind: WasiFn<(), ServerExecutionKind>,
|
||||
fetch_latest_server_version: WasiFn<(), Option<String>>,
|
||||
fetch_server_binary: WasiFn<(PathBuf, String), Result<LanguageServerBinary, String>>,
|
||||
cached_server_binary: WasiFn<PathBuf, Option<LanguageServerBinary>>,
|
||||
|
@ -47,7 +46,6 @@ impl PluginLspAdapter {
|
|||
pub async fn new(mut plugin: Plugin, executor: Arc<Background>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
name: plugin.function("name")?,
|
||||
server_execution_kind: plugin.function("server_execution_kind")?,
|
||||
fetch_latest_server_version: plugin.function("fetch_latest_server_version")?,
|
||||
fetch_server_binary: plugin.function("fetch_server_binary")?,
|
||||
cached_server_binary: plugin.function("cached_server_binary")?,
|
||||
|
@ -72,15 +70,6 @@ impl LspAdapter for PluginLspAdapter {
|
|||
LanguageServerName(name.into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
self.runtime
|
||||
.lock()
|
||||
.await
|
||||
.call(&self.server_execution_kind, ())
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
|
||||
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
|
@ -6,7 +6,7 @@ use async_tar::Archive;
|
|||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::{io::BufReader, StreamExt};
|
||||
use language::{LanguageServerBinary, LanguageServerName, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName};
|
||||
use smol::fs;
|
||||
use util::{async_iife, ResultExt};
|
||||
|
||||
|
@ -15,7 +15,7 @@ use super::installation::{latest_github_release, GitHubLspBinaryVersion};
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct LuaLspAdapter;
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
fn server_binary_arguments() -> Vec<OsString> {
|
||||
vec![
|
||||
"--logpath=~/lua-language-server.log".into(),
|
||||
"--loglevel=trace".into(),
|
||||
|
@ -28,10 +28,6 @@ impl super::LspAdapter for LuaLspAdapter {
|
|||
LanguageServerName("lua-language-server".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
159
crates/zed/src/languages/node_runtime.rs
Normal file
159
crates/zed/src/languages/node_runtime.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use client::http::HttpClient;
|
||||
use futures::{future::Shared, FutureExt, TryFutureExt};
|
||||
use gpui::{executor::Background, Task};
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
use smol::{fs, io::BufReader};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct NpmInfo {
|
||||
#[serde(default)]
|
||||
dist_tags: NpmInfoDistTags,
|
||||
versions: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct NpmInfoDistTags {
|
||||
latest: Option<String>,
|
||||
}
|
||||
|
||||
pub struct NodeRuntime {
|
||||
http: Arc<dyn HttpClient>,
|
||||
background: Arc<Background>,
|
||||
installation_path: Mutex<Option<Shared<Task<Result<PathBuf, Arc<anyhow::Error>>>>>>,
|
||||
}
|
||||
|
||||
impl NodeRuntime {
|
||||
pub fn new(http: Arc<dyn HttpClient>, background: Arc<Background>) -> Arc<NodeRuntime> {
|
||||
Arc::new(NodeRuntime {
|
||||
http,
|
||||
background,
|
||||
installation_path: Mutex::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn binary_path(&self) -> Result<PathBuf> {
|
||||
let installation_path = self.install_if_needed().await?;
|
||||
Ok(installation_path.join("bin/node"))
|
||||
}
|
||||
|
||||
pub async fn npm_package_latest_version(&self, name: &str) -> Result<String> {
|
||||
let installation_path = self.install_if_needed().await?;
|
||||
let node_binary = installation_path.join("bin/node");
|
||||
let npm_file = installation_path.join("bin/npm");
|
||||
|
||||
let output = smol::process::Command::new(node_binary)
|
||||
.arg(npm_file)
|
||||
.args(["-fetch-retry-mintimeout", "2000"])
|
||||
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||
.args(["-fetch-timeout", "5000"])
|
||||
.args(["info", name, "--json"])
|
||||
.output()
|
||||
.await
|
||||
.context("failed to run npm info")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!(
|
||||
"failed to execute npm info:\nstdout: {:?}\nstderr: {:?}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))?;
|
||||
}
|
||||
let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
|
||||
info.dist_tags
|
||||
.latest
|
||||
.or_else(|| info.versions.pop())
|
||||
.ok_or_else(|| anyhow!("no version found for npm package {}", name))
|
||||
}
|
||||
|
||||
pub async fn npm_install_packages(
|
||||
&self,
|
||||
packages: impl IntoIterator<Item = (&str, &str)>,
|
||||
directory: &Path,
|
||||
) -> Result<()> {
|
||||
let installation_path = self.install_if_needed().await?;
|
||||
let node_binary = installation_path.join("bin/node");
|
||||
let npm_file = installation_path.join("bin/npm");
|
||||
|
||||
let output = smol::process::Command::new(node_binary)
|
||||
.arg(npm_file)
|
||||
.args(["-fetch-retry-mintimeout", "2000"])
|
||||
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||
.args(["-fetch-timeout", "5000"])
|
||||
.arg("install")
|
||||
.arg("--prefix")
|
||||
.arg(directory)
|
||||
.args(
|
||||
packages
|
||||
.into_iter()
|
||||
.map(|(name, version)| format!("{name}@{version}")),
|
||||
)
|
||||
.output()
|
||||
.await
|
||||
.context("failed to run npm install")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!(
|
||||
"failed to execute npm install:\nstdout: {:?}\nstderr: {:?}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn install_if_needed(&self) -> Result<PathBuf> {
|
||||
let task = self
|
||||
.installation_path
|
||||
.lock()
|
||||
.get_or_insert_with(|| {
|
||||
let http = self.http.clone();
|
||||
self.background
|
||||
.spawn(
|
||||
async move {
|
||||
let version = "v18.15.0";
|
||||
let arch = "arm64";
|
||||
|
||||
let folder_name = format!("node-{version}-darwin-{arch}");
|
||||
let node_containing_dir = util::paths::SUPPORT_DIR.join("node");
|
||||
let node_dir = node_containing_dir.join(folder_name);
|
||||
let node_binary = node_dir.join("bin/node");
|
||||
|
||||
if fs::metadata(&node_binary).await.is_err() {
|
||||
_ = fs::remove_dir_all(&node_containing_dir).await;
|
||||
fs::create_dir(&node_containing_dir)
|
||||
.await
|
||||
.context("error creating node containing dir")?;
|
||||
|
||||
let url = format!("https://nodejs.org/dist/{version}/node-{version}-darwin-{arch}.tar.gz");
|
||||
let mut response =
|
||||
http.get(&url, Default::default(), true)
|
||||
.await
|
||||
.context("error downloading Node binary tarball")?;
|
||||
|
||||
let decompressed_bytes =
|
||||
GzipDecoder::new(BufReader::new(response.body_mut()));
|
||||
let archive = Archive::new(decompressed_bytes);
|
||||
archive.unpack(&node_containing_dir).await?;
|
||||
}
|
||||
|
||||
anyhow::Ok(node_dir)
|
||||
}
|
||||
.map_err(Arc::new),
|
||||
)
|
||||
.shared()
|
||||
})
|
||||
.clone();
|
||||
|
||||
match task.await {
|
||||
Ok(path) => Ok(path),
|
||||
Err(error) => Err(anyhow!("{}", error)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,32 @@
|
|||
use super::installation::{npm_install_packages, npm_package_latest_version};
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use smol::fs;
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
pub struct PythonLspAdapter;
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
vec!["--stdio".into()]
|
||||
pub struct PythonLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
}
|
||||
|
||||
impl PythonLspAdapter {
|
||||
const BIN_PATH: &'static str = "node_modules/pyright/langserver.index.js";
|
||||
const SERVER_PATH: &'static str = "node_modules/pyright/langserver.index.js";
|
||||
|
||||
pub fn new(node: Arc<NodeRuntime>) -> Self {
|
||||
PythonLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -24,21 +35,17 @@ impl LspAdapter for PythonLspAdapter {
|
|||
LanguageServerName("pyright".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Node
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(npm_package_latest_version(http, "pyright").await?) as Box<_>)
|
||||
Ok(Box::new(self.node.npm_package_latest_version("pyright").await?) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<String>().unwrap();
|
||||
|
@ -46,10 +53,12 @@ impl LspAdapter for PythonLspAdapter {
|
|||
fs::create_dir_all(&version_dir)
|
||||
.await
|
||||
.context("failed to create version directory")?;
|
||||
let binary_path = version_dir.join(Self::BIN_PATH);
|
||||
let server_path = version_dir.join(Self::SERVER_PATH);
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
npm_install_packages(http, [("pyright", version.as_str())], &version_dir).await?;
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages([("pyright", version.as_str())], &version_dir)
|
||||
.await?;
|
||||
|
||||
if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
|
||||
while let Some(entry) = entries.next().await {
|
||||
|
@ -64,8 +73,8 @@ impl LspAdapter for PythonLspAdapter {
|
|||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -80,11 +89,11 @@ impl LspAdapter for PythonLspAdapter {
|
|||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let bin_path = last_version_dir.join(Self::BIN_PATH);
|
||||
if bin_path.exists() {
|
||||
let server_path = last_version_dir.join(Self::SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: bin_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
|
||||
pub struct RubyLanguageServer;
|
||||
|
@ -12,10 +12,6 @@ impl LspAdapter for RubyLanguageServer {
|
|||
LanguageServerName("solargraph".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -19,10 +19,6 @@ impl LspAdapter for RustLspAdapter {
|
|||
LanguageServerName("rust-analyzer".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Launch
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
|
|
|
@ -1,26 +1,39 @@
|
|||
use super::installation::{npm_install_packages, npm_package_latest_version};
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
["--stdio", "--tsserver-path", "node_modules/typescript/lib"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![
|
||||
server_path.into(),
|
||||
"--stdio".into(),
|
||||
"--tsserver-path".into(),
|
||||
"node_modules/typescript/lib".into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub struct TypeScriptLspAdapter;
|
||||
pub struct TypeScriptLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
}
|
||||
|
||||
impl TypeScriptLspAdapter {
|
||||
const OLD_BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.js";
|
||||
const NEW_BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.mjs";
|
||||
const OLD_SERVER_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.js";
|
||||
const NEW_SERVER_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.mjs";
|
||||
|
||||
pub fn new(node: Arc<NodeRuntime>) -> Self {
|
||||
TypeScriptLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
struct Versions {
|
||||
|
@ -34,24 +47,23 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
LanguageServerName("typescript-language-server".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Node
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(Versions {
|
||||
typescript_version: npm_package_latest_version(http.clone(), "typescript").await?,
|
||||
server_version: npm_package_latest_version(http, "typescript-language-server").await?,
|
||||
typescript_version: self.node.npm_package_latest_version("typescript").await?,
|
||||
server_version: self
|
||||
.node
|
||||
.npm_package_latest_version("typescript-language-server")
|
||||
.await?,
|
||||
}) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
versions: Box<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let versions = versions.downcast::<Versions>().unwrap();
|
||||
|
@ -62,21 +74,21 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
fs::create_dir_all(&version_dir)
|
||||
.await
|
||||
.context("failed to create version directory")?;
|
||||
let binary_path = version_dir.join(Self::NEW_BIN_PATH);
|
||||
let server_path = version_dir.join(Self::NEW_SERVER_PATH);
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
npm_install_packages(
|
||||
http,
|
||||
[
|
||||
("typescript", versions.typescript_version.as_str()),
|
||||
(
|
||||
"typescript-language-server",
|
||||
versions.server_version.as_str(),
|
||||
),
|
||||
],
|
||||
&version_dir,
|
||||
)
|
||||
.await?;
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
[
|
||||
("typescript", versions.typescript_version.as_str()),
|
||||
(
|
||||
"typescript-language-server",
|
||||
versions.server_version.as_str(),
|
||||
),
|
||||
],
|
||||
&version_dir,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
|
||||
while let Some(entry) = entries.next().await {
|
||||
|
@ -91,8 +103,8 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -107,17 +119,17 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let old_bin_path = last_version_dir.join(Self::OLD_BIN_PATH);
|
||||
let new_bin_path = last_version_dir.join(Self::NEW_BIN_PATH);
|
||||
if new_bin_path.exists() {
|
||||
let old_server_path = last_version_dir.join(Self::OLD_SERVER_PATH);
|
||||
let new_server_path = last_version_dir.join(Self::NEW_SERVER_PATH);
|
||||
if new_server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: new_bin_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&new_server_path),
|
||||
})
|
||||
} else if old_bin_path.exists() {
|
||||
} else if old_server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: old_bin_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&old_server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
|
|
|
@ -1,25 +1,36 @@
|
|||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use gpui::MutableAppContext;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, ServerExecutionKind};
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::Value;
|
||||
use settings::Settings;
|
||||
use smol::fs;
|
||||
use std::{any::Any, future, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
future,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
use super::installation::{npm_install_packages, npm_package_latest_version};
|
||||
|
||||
fn server_binary_arguments() -> Vec<String> {
|
||||
vec!["--stdio".into()]
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
pub struct YamlLspAdapter;
|
||||
pub struct YamlLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
}
|
||||
|
||||
impl YamlLspAdapter {
|
||||
const BIN_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server";
|
||||
const SERVER_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server";
|
||||
|
||||
pub fn new(node: Arc<NodeRuntime>) -> Self {
|
||||
YamlLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -28,21 +39,21 @@ impl LspAdapter for YamlLspAdapter {
|
|||
LanguageServerName("yaml-language-server".into())
|
||||
}
|
||||
|
||||
async fn server_execution_kind(&self) -> ServerExecutionKind {
|
||||
ServerExecutionKind::Node
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(npm_package_latest_version(http, "yaml-language-server").await?) as Box<_>)
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("yaml-language-server")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<String>().unwrap();
|
||||
|
@ -50,15 +61,12 @@ impl LspAdapter for YamlLspAdapter {
|
|||
fs::create_dir_all(&version_dir)
|
||||
.await
|
||||
.context("failed to create version directory")?;
|
||||
let binary_path = version_dir.join(Self::BIN_PATH);
|
||||
let server_path = version_dir.join(Self::SERVER_PATH);
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
npm_install_packages(
|
||||
http,
|
||||
[("yaml-language-server", version.as_str())],
|
||||
&version_dir,
|
||||
)
|
||||
.await?;
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages([("yaml-language-server", version.as_str())], &version_dir)
|
||||
.await?;
|
||||
|
||||
if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
|
||||
while let Some(entry) = entries.next().await {
|
||||
|
@ -73,8 +81,8 @@ impl LspAdapter for YamlLspAdapter {
|
|||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -89,11 +97,11 @@ impl LspAdapter for YamlLspAdapter {
|
|||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let bin_path = last_version_dir.join(Self::BIN_PATH);
|
||||
if bin_path.exists() {
|
||||
let server_path = last_version_dir.join(Self::SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: bin_path,
|
||||
arguments: server_binary_arguments(),
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
|
|
|
@ -81,15 +81,6 @@ fn main() {
|
|||
})
|
||||
};
|
||||
|
||||
let node_path = {
|
||||
let http = http.clone();
|
||||
app.background().spawn(async move {
|
||||
languages::ensure_node_installation_dir(http.clone())
|
||||
.await
|
||||
.log_err()
|
||||
})
|
||||
};
|
||||
|
||||
let (cli_connections_tx, mut cli_connections_rx) = mpsc::unbounded();
|
||||
let (open_paths_tx, mut open_paths_rx) = mpsc::unbounded();
|
||||
app.on_open_urls(move |urls, _| {
|
||||
|
@ -144,11 +135,16 @@ fn main() {
|
|||
}
|
||||
|
||||
let client = client::Client::new(http.clone(), cx);
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded, node_path);
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||
languages.set_executor(cx.background().clone());
|
||||
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
||||
let languages = Arc::new(languages);
|
||||
languages::init(languages.clone(), themes.clone());
|
||||
languages::init(
|
||||
http.clone(),
|
||||
cx.background().clone(),
|
||||
languages.clone(),
|
||||
themes.clone(),
|
||||
);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
||||
|
||||
cx.set_global(client.clone());
|
||||
|
|
|
@ -652,6 +652,7 @@ fn open_bundled_file(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use assets::Assets;
|
||||
use client::test::FakeHttpClient;
|
||||
use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
|
||||
use gpui::{
|
||||
executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
|
||||
|
@ -1850,7 +1851,12 @@ mod tests {
|
|||
languages.set_executor(cx.background().clone());
|
||||
let languages = Arc::new(languages);
|
||||
let themes = ThemeRegistry::new((), cx.font_cache().clone());
|
||||
languages::init(languages.clone(), themes);
|
||||
languages::init(
|
||||
FakeHttpClient::with_404_response(),
|
||||
cx.background().clone(),
|
||||
languages.clone(),
|
||||
themes,
|
||||
);
|
||||
for name in languages.language_names() {
|
||||
languages.language_for_name(&name);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use serde::Deserialize;
|
|||
#[import]
|
||||
fn command(string: &str) -> Option<Vec<u8>>;
|
||||
|
||||
const BIN_PATH: &str = "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
|
||||
const SERVER_PATH: &str = "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
|
||||
|
||||
#[export]
|
||||
pub fn name() -> &'static str {
|
||||
|
@ -38,7 +38,7 @@ pub fn fetch_server_binary(container_dir: PathBuf, version: String) -> Result<Pa
|
|||
let version_dir = container_dir.join(version.as_str());
|
||||
fs::create_dir_all(&version_dir)
|
||||
.map_err(|_| "failed to create version directory".to_string())?;
|
||||
let binary_path = version_dir.join(BIN_PATH);
|
||||
let binary_path = version_dir.join(SERVER_PATH);
|
||||
|
||||
if fs::metadata(&binary_path).is_err() {
|
||||
let output = command(&format!(
|
||||
|
@ -76,9 +76,9 @@ pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
|
|||
}
|
||||
|
||||
let last_version_dir = last_version_dir?;
|
||||
let bin_path = last_version_dir.join(BIN_PATH);
|
||||
if bin_path.exists() {
|
||||
Some(bin_path)
|
||||
let server_path = last_version_dir.join(SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Some(server_path)
|
||||
} else {
|
||||
println!("no binary found");
|
||||
None
|
||||
|
|
Loading…
Reference in a new issue