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