diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 00bc29086c..a99c80c001 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -14,8 +14,8 @@ mod lua; mod python; mod ruby; mod rust; - mod typescript; +mod yaml; // 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 @@ -131,8 +131,8 @@ pub fn init(languages: Arc) { ( "yaml", tree_sitter_yaml::language(), - None, // - ) + Some(Box::new(yaml::YamlLspAdapter)), + ), ] { languages.register(name, load_config(name), grammar, lsp_adapter, load_queries); } diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs new file mode 100644 index 0000000000..6900b6b965 --- /dev/null +++ b/crates/zed/src/languages/yaml.rs @@ -0,0 +1,94 @@ +use std::{any::Any, path::PathBuf, sync::Arc}; + +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use client::http::HttpClient; +use futures::StreamExt; +use serde_json::json; +use smol::fs; + +use language::{LanguageServerName, LspAdapter}; +use util::ResultExt; + +use super::installation::{npm_install_packages, npm_package_latest_version}; + +pub struct YamlLspAdapter; + +impl YamlLspAdapter { + const BIN_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server"; +} + +#[async_trait] +impl LspAdapter for YamlLspAdapter { + async fn name(&self) -> LanguageServerName { + LanguageServerName("yaml-language-server".into()) + } + + async fn server_args(&self) -> Vec { + vec!["--stdio".into()] + } + + async fn fetch_latest_server_version( + &self, + _: Arc, + ) -> Result> { + Ok(Box::new(npm_package_latest_version("yaml-language-server").await?) as Box<_>) + } + + async fn fetch_server_binary( + &self, + version: Box, + _: Arc, + container_dir: PathBuf, + ) -> Result { + let version = version.downcast::().unwrap(); + let version_dir = container_dir.join(version.as_str()); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); + + if fs::metadata(&binary_path).await.is_err() { + 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 { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); + } + } + } + } + } + + Ok(binary_path) + } + + async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + 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() { + Ok(bin_path) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() + } +}