use super::installation::{npm_install_packages, npm_package_latest_version}; use anyhow::{anyhow, Context, Result}; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::executor::{self, Background}; use isahc::http::version; use language::{LanguageServerName, LspAdapter}; use parking_lot::{Mutex, RwLock}; use plugin_runtime::{Wasi, WasiPlugin}; use serde_json::json; use std::fs; use std::{any::Any, path::PathBuf, sync::Arc}; use util::{ResultExt, TryFutureExt}; pub async fn new_json(executor: Arc) -> Result { let plugin = WasiPlugin { module: include_bytes!("../../../../plugins/bin/json_language.wasm").to_vec(), wasi_ctx: Wasi::default_ctx(), }; PluginLspAdapter::new(plugin, executor).await } pub struct PluginLspAdapter { runtime: Arc>, executor: Arc, } impl PluginLspAdapter { pub async fn new(plugin: WasiPlugin, executor: Arc) -> Result { Ok(Self { runtime: Arc::new(Mutex::new(Wasi::init(plugin).await?)), executor, }) } } struct Versions { language_version: String, server_version: String, } macro_rules! call_block { ($self:ident, $name:expr, $arg:expr) => { $self .executor .block(async { $self.runtime.lock().call($name, $arg).await }) }; } impl LspAdapter for PluginLspAdapter { fn name(&self) -> LanguageServerName { let name: String = call_block!(self, "name", ()).unwrap(); LanguageServerName(name.into()) } fn server_args<'a>(&'a self) -> Vec { call_block!(self, "server_args", ()).unwrap() } fn fetch_latest_server_version( &self, _: Arc, ) -> BoxFuture<'static, Result>> { let versions: Result> = call_block!(self, "fetch_latest_server_version", ()); async move { // let versions: Result> = self // .runtime // .lock() // .call::<_, Option>("fetch_latest_server_version", ()) // .await; versions .map_err(|e| anyhow!("{}", e))? .ok_or_else(|| anyhow!("Could not fetch latest server version")) .map(|v| Box::new(v) as Box<_>) } .boxed() } fn fetch_server_binary( &self, version: Box, _: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result> { let version = version.downcast::().unwrap(); let mut runtime = self.runtime.lock(); let result = (|| { let handle = runtime.attach_path(&container_dir)?; let result: Option = call_block!(self, "fetch_server_binary", (container_dir, version))?; runtime.remove_resource(handle)?; result.ok_or_else(|| anyhow!("Could not load cached server binary")) })(); async move { result }.boxed() } fn cached_server_binary(&self, container_dir: PathBuf) -> BoxFuture<'static, Option> { let mut runtime = self.runtime.lock(); let result: Option = (|| { let handle = runtime.attach_path(&container_dir).ok()?; let result: Option = call_block!(self, "cached_server_binary", container_dir).ok()?; runtime.remove_resource(handle).ok()?; result })(); async move { result }.boxed() } fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} fn label_for_completion( &self, item: &lsp::CompletionItem, language: &language::Language, ) -> Option { use lsp::CompletionItemKind as Kind; let len = item.label.len(); let grammar = language.grammar()?; let kind = format!("{:?}", item.kind?); let name: String = call_block!(self, "label_for_completion", kind).log_err()?; let highlight_id = grammar.highlight_id_for_name(&name)?; Some(language::CodeLabel { text: item.label.clone(), runs: vec![(0..len, highlight_id)], filter_range: 0..len, }) } fn initialization_options(&self) -> Option { let string: String = call_block!(self, "initialization_options", ()).log_err()?; serde_json::from_str(&string).ok() } }