From edd6c85af7244be5210166be9b3b6d24dee064fc Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 23 Mar 2023 17:32:13 -0400 Subject: [PATCH] Initial running of servers on downloaded Node --- crates/collab/src/tests.rs | 2 +- crates/language/src/language.rs | 41 ++++++++++++++++------ crates/lsp/src/lsp.rs | 20 +++++------ crates/util/src/paths.rs | 1 + crates/zed/src/languages.rs | 2 ++ crates/zed/src/languages/installation.rs | 44 ++++++++++++++++++++++-- crates/zed/src/main.rs | 11 +++++- crates/zed/src/zed.rs | 2 +- 8 files changed, 97 insertions(+), 26 deletions(-) diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 6ebfdc90b7..b31feaa9b0 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -188,7 +188,7 @@ impl TestServer { let app_state = Arc::new(workspace::AppState { client: client.clone(), user_store: user_store.clone(), - languages: Arc::new(LanguageRegistry::new(Task::ready(()))), + languages: Arc::new(LanguageRegistry::test()), themes: ThemeRegistry::new((), cx.font_cache()), fs: fs.clone(), build_window_options: |_, _, _| Default::default(), diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 201eb8c7fc..c62bbbb146 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -492,6 +492,7 @@ pub struct LanguageRegistry { lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)>, login_shell_env_loaded: Shared>, + node_path: Shared>>, #[allow(clippy::type_complexity)] lsp_binary_paths: Mutex< HashMap< @@ -513,7 +514,7 @@ struct LanguageRegistryState { } impl LanguageRegistry { - pub fn new(login_shell_env_loaded: Task<()>) -> Self { + pub fn new(login_shell_env_loaded: Task<()>, node_path: Task>) -> Self { let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16); Self { state: RwLock::new(LanguageRegistryState { @@ -529,6 +530,7 @@ 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, } @@ -536,7 +538,7 @@ impl LanguageRegistry { #[cfg(any(test, feature = "test-support"))] pub fn test() -> Self { - Self::new(Task::ready(())) + Self::new(Task::ready(()), Task::Ready(None)) } pub fn set_executor(&mut self, executor: Arc) { @@ -802,8 +804,12 @@ 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(); + Some(cx.spawn(|cx| async move { login_shell_env_loaded.await; + let node_path = node_path.await; + let server_binary_path = this .lsp_binary_paths .lock() @@ -824,14 +830,27 @@ impl LanguageRegistry { .map_err(|e| anyhow!(e)); let server_binary_path = server_binary_path.await?; - let server_args = &adapter.server_args; - let server = lsp::LanguageServer::new( - server_id, - &server_binary_path, - server_args, - &root_path, - cx, - )?; + 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"); + dbg!(&node_binary); + 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(&adapter.server_args); + let server = lsp::LanguageServer::new(server_id, server_name, command, &root_path, cx)?; + Ok(server) })) } @@ -1528,7 +1547,7 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_language_loading(cx: &mut TestAppContext) { - let mut languages = LanguageRegistry::new(Task::ready(())); + let mut languages = LanguageRegistry::test(); languages.set_executor(cx.background()); let languages = Arc::new(languages); languages.register( diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index ad94423e11..87dbf95b11 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -105,10 +105,10 @@ struct Error { } impl LanguageServer { - pub fn new>( + pub fn new( server_id: usize, - binary_path: &Path, - args: &[T], + server_name: Option, + mut command: process::Command, root_path: &Path, cx: AsyncAppContext, ) -> Result { @@ -117,18 +117,17 @@ impl LanguageServer { } else { root_path.parent().unwrap_or_else(|| Path::new("/")) }; - let mut server = process::Command::new(binary_path) + + let mut server = dbg!(command .current_dir(working_dir) - .args(args) .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(); - let mut server = Self::new_internal( server_id, stdin, @@ -147,8 +146,9 @@ impl LanguageServer { ); }, ); - if let Some(name) = binary_path.file_name() { - server.name = name.to_string_lossy().to_string(); + + if let Some(name) = server_name { + server.name = name; } Ok(server) } diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 8698d6891e..63c3c6d884 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -4,6 +4,7 @@ lazy_static::lazy_static! { pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory"); pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed"); pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed"); + pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed"); pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages"); pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db"); pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json"); diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 1acee4bad4..6bf1d94c77 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -18,6 +18,8 @@ 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 diff --git a/crates/zed/src/languages/installation.rs b/crates/zed/src/languages/installation.rs index c5aff17e56..54fd6ddff9 100644 --- a/crates/zed/src/languages/installation.rs +++ b/crates/zed/src/languages/installation.rs @@ -1,9 +1,15 @@ use anyhow::{anyhow, Context, Result}; +use async_compression::futures::bufread::GzipDecoder; +use async_tar::Archive; use client::http::HttpClient; - +use futures::{io::BufReader, StreamExt}; use serde::Deserialize; +use smol::fs::{self, File}; use smol::io::AsyncReadExt; -use std::{path::Path, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; pub struct GitHubLspBinaryVersion { pub name: String, @@ -35,6 +41,40 @@ pub(crate) struct GithubReleaseAsset { pub browser_download_url: String, } +pub async fn ensure_node_installation_dir(http: Arc) -> Result { + 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 = dbg!(util::paths::SUPPORT_DIR.join("node")); + let node_dir = dbg!(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"); + dbg!(&url); + 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"); + } + + eprintln!("returning"); + Ok(dbg!(node_dir)) +} + pub async fn npm_package_latest_version(name: &str) -> Result { let output = smol::process::Command::new("npm") .args(["-fetch-retry-mintimeout", "2000"]) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b1813bab50..d794e4c39d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -81,6 +81,15 @@ 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, _| { @@ -135,7 +144,7 @@ fn main() { } let client = client::Client::new(http.clone(), cx); - let mut languages = LanguageRegistry::new(login_shell_env_loaded); + let mut languages = LanguageRegistry::new(login_shell_env_loaded, node_path); languages.set_executor(cx.background().clone()); languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b0239d234c..a28b407328 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1846,7 +1846,7 @@ mod tests { #[gpui::test] fn test_bundled_languages(cx: &mut MutableAppContext) { - let mut languages = LanguageRegistry::new(Task::ready(())); + let mut languages = LanguageRegistry::test(); languages.set_executor(cx.background().clone()); let languages = Arc::new(languages); let themes = ThemeRegistry::new((), cx.font_cache().clone());