From c72d33e0294483a29d96a371f46232c98c99bfff Mon Sep 17 00:00:00 2001 From: Julia Date: Sun, 26 Mar 2023 23:59:49 -0400 Subject: [PATCH] Initial impl of `NodeRuntime` w/JSON borked and a deadlock :) Co-Authored-By: Antonio Scandurra --- crates/language/src/language.rs | 64 +++----- crates/lsp/src/lsp.rs | 17 ++- crates/zed/src/languages.rs | 36 +++-- crates/zed/src/languages/c.rs | 4 - crates/zed/src/languages/elixir.rs | 4 - crates/zed/src/languages/go.rs | 8 +- crates/zed/src/languages/html.rs | 70 +++++---- crates/zed/src/languages/installation.rs | 119 +-------------- crates/zed/src/languages/json.rs | 41 +++-- crates/zed/src/languages/language_plugin.rs | 13 +- crates/zed/src/languages/lua.rs | 10 +- crates/zed/src/languages/node_runtime.rs | 159 ++++++++++++++++++++ crates/zed/src/languages/python.rs | 55 ++++--- crates/zed/src/languages/ruby.rs | 6 +- crates/zed/src/languages/rust.rs | 4 - crates/zed/src/languages/typescript.rs | 98 ++++++------ crates/zed/src/languages/yaml.rs | 66 ++++---- crates/zed/src/main.rs | 18 +-- crates/zed/src/zed.rs | 8 +- plugins/json_language/src/lib.rs | 10 +- 20 files changed, 435 insertions(+), 375 deletions(-) create mode 100644 crates/zed/src/languages/node_runtime.rs diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 24fe2cb7ba..dc9a74e144 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -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, + pub arguments: Vec, } /// 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, @@ -494,7 +493,6 @@ 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< @@ -516,7 +514,7 @@ struct LanguageRegistryState { } impl LanguageRegistry { - pub fn new(login_shell_env_loaded: Task<()>, node_path: Task>) -> 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) { @@ -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, language: Arc, http_client: Arc, @@ -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, language: Arc, http_client: Arc, @@ -1471,10 +1459,6 @@ impl LspAdapter for Arc { LanguageServerName(self.name.into()) } - async fn server_execution_kind(&self) -> ServerExecutionKind { - ServerExecutionKind::Launch - } - async fn fetch_latest_server_version( &self, _: Arc, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 87dbf95b11..80bf0a70f6 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, - server_name: Option, - mut command: process::Command, + binary_path: &Path, + arguments: &[T], root_path: &Path, cx: AsyncAppContext, ) -> Result { @@ -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) } diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 6bf1d94c77..8eae806fdb 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -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, themes: Arc) { +pub fn init( + http: Arc, + background: Arc, + languages: Arc, + themes: Arc, +) { + let node_runtime = NodeRuntime::new(http, background); + for (name, grammar, lsp_adapter) in [ ( "c", @@ -65,6 +74,7 @@ pub fn init(languages: Arc, themes: Arc) { "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, themes: Arc) { ( "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, themes: Arc) { ( "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, themes: Arc) { ( "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); diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index afd94604d9..1799dc2973 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -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, diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index 390a35fa3d..75b6ff18b2 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -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, diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index f51c57ab8c..b887aa1017 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -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 { +fn server_binary_arguments() -> Vec { 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, diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs index 8c0f8e026d..a2cfbac96b 100644 --- a/crates/zed/src/languages/html.rs +++ b/crates/zed/src/languages/html.rs @@ -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 { - vec!["--stdio".into()] +fn server_binary_arguments(server_path: &Path) -> Vec { + vec![server_path.into(), "--stdio".into()] } -pub struct HtmlLspAdapter; +pub struct HtmlLspAdapter { + node: Arc, +} 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) -> 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, + _: Arc, ) -> Result> { - 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, - http: Arc, + _: Arc, container_dir: PathBuf, ) -> Result { let version = version.downcast::().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!( diff --git a/crates/zed/src/languages/installation.rs b/crates/zed/src/languages/installation.rs index 0eb6e0b760..8fdef50790 100644 --- a/crates/zed/src/languages/installation.rs +++ b/crates/zed/src/languages/installation.rs @@ -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, -} - -#[derive(Deserialize, Default)] -struct NpmInfoDistTags { - latest: Option, -} - #[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) -> 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 = 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, name: &str) -> 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"]) - .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, - packages: impl IntoIterator, - 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, diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 7c07165b2b..10665d96b6 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -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 { - vec!["--stdio".into()] +fn server_binary_arguments(server_path: &Path) -> Vec { + dbg!(vec![server_path.into(), "--stdio".into()]) } pub struct JsonLspAdapter { + node: Arc, languages: Arc, themes: Arc, } impl JsonLspAdapter { - pub fn new(languages: Arc, themes: Arc) -> Self { - Self { languages, themes } + pub fn new( + node: Arc, + languages: Arc, + themes: Arc, + ) -> 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, @@ -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 diff --git a/crates/zed/src/languages/language_plugin.rs b/crates/zed/src/languages/language_plugin.rs index 9f12878990..38f50d2d88 100644 --- a/crates/zed/src/languages/language_plugin.rs +++ b/crates/zed/src/languages/language_plugin.rs @@ -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) -> Result { pub struct PluginLspAdapter { name: WasiFn<(), String>, - server_execution_kind: WasiFn<(), ServerExecutionKind>, fetch_latest_server_version: WasiFn<(), Option>, fetch_server_binary: WasiFn<(PathBuf, String), Result>, cached_server_binary: WasiFn>, @@ -47,7 +46,6 @@ impl PluginLspAdapter { pub async fn new(mut plugin: Plugin, executor: Arc) -> Result { 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, diff --git a/crates/zed/src/languages/lua.rs b/crates/zed/src/languages/lua.rs index 2ee67016dd..37c3e36489 100644 --- a/crates/zed/src/languages/lua.rs +++ b/crates/zed/src/languages/lua.rs @@ -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 { +fn server_binary_arguments() -> Vec { 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, diff --git a/crates/zed/src/languages/node_runtime.rs b/crates/zed/src/languages/node_runtime.rs new file mode 100644 index 0000000000..da4ce42b43 --- /dev/null +++ b/crates/zed/src/languages/node_runtime.rs @@ -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, +} + +#[derive(Deserialize, Default)] +pub struct NpmInfoDistTags { + latest: Option, +} + +pub struct NodeRuntime { + http: Arc, + background: Arc, + installation_path: Mutex>>>>>, +} + +impl NodeRuntime { + pub fn new(http: Arc, background: Arc) -> Arc { + Arc::new(NodeRuntime { + http, + background, + installation_path: Mutex::new(None), + }) + } + + pub async fn binary_path(&self) -> Result { + 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 { + 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, + 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 { + 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)), + } + } +} diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index ece8f70362..9a09c63bb6 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -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 { + vec![server_path.into(), "--stdio".into()] +} -fn server_binary_arguments() -> Vec { - vec!["--stdio".into()] +pub struct PythonLspAdapter { + node: Arc, } 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) -> 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, + _: Arc, ) -> Result> { - 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, - http: Arc, + _: Arc, container_dir: PathBuf, ) -> Result { let version = version.downcast::().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!( diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs index f30a536746..662c1f464d 100644 --- a/crates/zed/src/languages/ruby.rs +++ b/crates/zed/src/languages/ruby.rs @@ -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, diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 5eac0fca0a..2fe063ed5e 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -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, diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 316f5d7627..f9baf4f8f7 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -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 { - ["--stdio", "--tsserver-path", "node_modules/typescript/lib"] - .into_iter() - .map(Into::into) - .collect() +fn server_binary_arguments(server_path: &Path) -> Vec { + vec![ + server_path.into(), + "--stdio".into(), + "--tsserver-path".into(), + "node_modules/typescript/lib".into(), + ] } -pub struct TypeScriptLspAdapter; +pub struct TypeScriptLspAdapter { + node: Arc, +} 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) -> 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, + _: Arc, ) -> Result> { 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, - http: Arc, + _: Arc, container_dir: PathBuf, ) -> Result { let versions = versions.downcast::().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!( diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index a9b9fbe1ef..b6e82842de 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -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 { - vec!["--stdio".into()] +fn server_binary_arguments(server_path: &Path) -> Vec { + vec![server_path.into(), "--stdio".into()] } -pub struct YamlLspAdapter; +pub struct YamlLspAdapter { + node: Arc, +} 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) -> 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, + _: Arc, ) -> Result> { - 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, - http: Arc, + _: Arc, container_dir: PathBuf, ) -> Result { let version = version.downcast::().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!( diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index d794e4c39d..fb6c6227c3 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -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()); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index fd6b560272..788be77e75 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -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); } diff --git a/plugins/json_language/src/lib.rs b/plugins/json_language/src/lib.rs index b3c70da7a1..e18d9ce74b 100644 --- a/plugins/json_language/src/lib.rs +++ b/plugins/json_language/src/lib.rs @@ -6,7 +6,7 @@ use serde::Deserialize; #[import] fn command(string: &str) -> Option>; -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 Option { } 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