yaml.rs

  1use anyhow::{anyhow, Result};
  2use async_trait::async_trait;
  3use futures::StreamExt;
  4use gpui::AppContext;
  5use language::{
  6    language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
  7};
  8use lsp::LanguageServerBinary;
  9use node_runtime::NodeRuntime;
 10use serde_json::Value;
 11use smol::fs;
 12use std::{
 13    any::Any,
 14    ffi::OsString,
 15    path::{Path, PathBuf},
 16    sync::Arc,
 17};
 18use util::{async_maybe, ResultExt};
 19
 20const SERVER_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server";
 21
 22fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
 23    vec![server_path.into(), "--stdio".into()]
 24}
 25
 26pub struct YamlLspAdapter {
 27    node: Arc<dyn NodeRuntime>,
 28}
 29
 30impl YamlLspAdapter {
 31    pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
 32        YamlLspAdapter { node }
 33    }
 34}
 35
 36#[async_trait]
 37impl LspAdapter for YamlLspAdapter {
 38    fn name(&self) -> LanguageServerName {
 39        LanguageServerName("yaml-language-server".into())
 40    }
 41
 42    fn short_name(&self) -> &'static str {
 43        "yaml"
 44    }
 45
 46    async fn fetch_latest_server_version(
 47        &self,
 48        _: &dyn LspAdapterDelegate,
 49    ) -> Result<Box<dyn 'static + Any + Send>> {
 50        Ok(Box::new(
 51            self.node
 52                .npm_package_latest_version("yaml-language-server")
 53                .await?,
 54        ) as Box<_>)
 55    }
 56
 57    async fn fetch_server_binary(
 58        &self,
 59        version: Box<dyn 'static + Send + Any>,
 60        container_dir: PathBuf,
 61        _: &dyn LspAdapterDelegate,
 62    ) -> Result<LanguageServerBinary> {
 63        let version = version.downcast::<String>().unwrap();
 64        let server_path = container_dir.join(SERVER_PATH);
 65
 66        if fs::metadata(&server_path).await.is_err() {
 67            self.node
 68                .npm_install_packages(
 69                    &container_dir,
 70                    &[("yaml-language-server", version.as_str())],
 71                )
 72                .await?;
 73        }
 74
 75        Ok(LanguageServerBinary {
 76            path: self.node.binary_path().await?,
 77            env: None,
 78            arguments: server_binary_arguments(&server_path),
 79        })
 80    }
 81
 82    async fn cached_server_binary(
 83        &self,
 84        container_dir: PathBuf,
 85        _: &dyn LspAdapterDelegate,
 86    ) -> Option<LanguageServerBinary> {
 87        get_cached_server_binary(container_dir, &*self.node).await
 88    }
 89
 90    async fn installation_test_binary(
 91        &self,
 92        container_dir: PathBuf,
 93    ) -> Option<LanguageServerBinary> {
 94        get_cached_server_binary(container_dir, &*self.node).await
 95    }
 96    fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
 97        serde_json::json!({
 98            "yaml": {
 99                "keyOrdering": false
100            },
101            "[yaml]": {
102                "editor.tabSize": all_language_settings(None, cx)
103                    .language(Some("YAML"))
104                    .tab_size,
105            }
106        })
107    }
108}
109
110async fn get_cached_server_binary(
111    container_dir: PathBuf,
112    node: &dyn NodeRuntime,
113) -> Option<LanguageServerBinary> {
114    async_maybe!({
115        let mut last_version_dir = None;
116        let mut entries = fs::read_dir(&container_dir).await?;
117        while let Some(entry) = entries.next().await {
118            let entry = entry?;
119            if entry.file_type().await?.is_dir() {
120                last_version_dir = Some(entry.path());
121            }
122        }
123        let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
124        let server_path = last_version_dir.join(SERVER_PATH);
125        if server_path.exists() {
126            Ok(LanguageServerBinary {
127                path: node.binary_path().await?,
128                env: None,
129                arguments: server_binary_arguments(&server_path),
130            })
131        } else {
132            Err(anyhow!(
133                "missing executable in directory {:?}",
134                last_version_dir
135            ))
136        }
137    })
138    .await
139    .log_err()
140}