language_plugin.rs

  1use anyhow::{anyhow, Result};
  2use async_trait::async_trait;
  3use client::http::HttpClient;
  4use futures::lock::Mutex;
  5use gpui::executor::Background;
  6use language::{LanguageServerName, LspAdapter};
  7use plugin_runtime::{Plugin, PluginBuilder, WasiFn};
  8use std::{any::Any, path::PathBuf, sync::Arc};
  9use util::ResultExt;
 10
 11pub async fn new_json(executor: Arc<Background>) -> Result<PluginLspAdapter> {
 12    let plugin = PluginBuilder::new_with_default_ctx()?
 13        .host_function_async("command", |command: String| async move {
 14            let mut args = command.split(' ');
 15            let command = args.next().unwrap();
 16            smol::process::Command::new(command)
 17                .args(args)
 18                .output()
 19                .await
 20                .log_err()
 21                .map(|output| output.stdout)
 22        })?
 23        .init(
 24            true,
 25            include_bytes!("../../../../plugins/bin/json_language.wasm.pre"),
 26        )
 27        .await?;
 28
 29    PluginLspAdapter::new(plugin, executor).await
 30}
 31
 32pub struct PluginLspAdapter {
 33    name: WasiFn<(), String>,
 34    server_args: WasiFn<(), Vec<String>>,
 35    fetch_latest_server_version: WasiFn<(), Option<String>>,
 36    fetch_server_binary: WasiFn<(PathBuf, String), Result<PathBuf, String>>,
 37    cached_server_binary: WasiFn<PathBuf, Option<PathBuf>>,
 38    initialization_options: WasiFn<(), String>,
 39    executor: Arc<Background>,
 40    runtime: Arc<Mutex<Plugin>>,
 41}
 42
 43impl PluginLspAdapter {
 44    pub async fn new(mut plugin: Plugin, executor: Arc<Background>) -> Result<Self> {
 45        Ok(Self {
 46            name: plugin.function("name")?,
 47            server_args: plugin.function("server_args")?,
 48            fetch_latest_server_version: plugin.function("fetch_latest_server_version")?,
 49            fetch_server_binary: plugin.function("fetch_server_binary")?,
 50            cached_server_binary: plugin.function("cached_server_binary")?,
 51            initialization_options: plugin.function("initialization_options")?,
 52            executor,
 53            runtime: Arc::new(Mutex::new(plugin)),
 54        })
 55    }
 56}
 57
 58#[async_trait]
 59impl LspAdapter for PluginLspAdapter {
 60    async fn name(&self) -> LanguageServerName {
 61        let name: String = self
 62            .runtime
 63            .lock()
 64            .await
 65            .call(&self.name, ())
 66            .await
 67            .unwrap();
 68        LanguageServerName(name.into())
 69    }
 70
 71    async fn server_args<'a>(&'a self) -> Vec<String> {
 72        self.runtime
 73            .lock()
 74            .await
 75            .call(&self.server_args, ())
 76            .await
 77            .unwrap()
 78    }
 79
 80    async fn fetch_latest_server_version(
 81        &self,
 82        _: Arc<dyn HttpClient>,
 83    ) -> Result<Box<dyn 'static + Send + Any>> {
 84        let runtime = self.runtime.clone();
 85        let function = self.fetch_latest_server_version;
 86        self.executor
 87            .spawn(async move {
 88                let mut runtime = runtime.lock().await;
 89                let versions: Result<Option<String>> =
 90                    runtime.call::<_, Option<String>>(&function, ()).await;
 91                versions
 92                    .map_err(|e| anyhow!("{}", e))?
 93                    .ok_or_else(|| anyhow!("Could not fetch latest server version"))
 94                    .map(|v| Box::new(v) as Box<_>)
 95            })
 96            .await
 97    }
 98
 99    async fn fetch_server_binary(
100        &self,
101        version: Box<dyn 'static + Send + Any>,
102        _: Arc<dyn HttpClient>,
103        container_dir: PathBuf,
104    ) -> Result<PathBuf> {
105        let version = *version.downcast::<String>().unwrap();
106        let runtime = self.runtime.clone();
107        let function = self.fetch_server_binary;
108        self.executor
109            .spawn(async move {
110                let mut runtime = runtime.lock().await;
111                let handle = runtime.attach_path(&container_dir)?;
112                let result: Result<PathBuf, String> =
113                    runtime.call(&function, (container_dir, version)).await?;
114                runtime.remove_resource(handle)?;
115                result.map_err(|e| anyhow!("{}", e))
116            })
117            .await
118    }
119
120    async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
121        let runtime = self.runtime.clone();
122        let function = self.cached_server_binary;
123
124        self.executor
125            .spawn(async move {
126                let mut runtime = runtime.lock().await;
127                let handle = runtime.attach_path(&container_dir).ok()?;
128                let result: Option<PathBuf> = runtime.call(&function, container_dir).await.ok()?;
129                runtime.remove_resource(handle).ok()?;
130                result
131            })
132            .await
133    }
134
135    async fn initialization_options(&self) -> Option<serde_json::Value> {
136        let string: String = self
137            .runtime
138            .lock()
139            .await
140            .call(&self.initialization_options, ())
141            .await
142            .log_err()?;
143
144        serde_json::from_str(&string).ok()
145    }
146}