1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use collections::HashMap;
4use futures::lock::Mutex;
5use gpui::executor::Background;
6use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
7use lsp2::LanguageServerBinary;
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 fetch_latest_server_version: WasiFn<(), Option<String>>,
36 fetch_server_binary: WasiFn<(PathBuf, String), Result<LanguageServerBinary, String>>,
37 cached_server_binary: WasiFn<PathBuf, Option<LanguageServerBinary>>,
38 initialization_options: WasiFn<(), String>,
39 language_ids: WasiFn<(), Vec<(String, String)>>,
40 executor: Arc<Background>,
41 runtime: Arc<Mutex<Plugin>>,
42}
43
44impl PluginLspAdapter {
45 #[allow(unused)]
46 pub async fn new(mut plugin: Plugin, executor: Arc<Background>) -> Result<Self> {
47 Ok(Self {
48 name: plugin.function("name")?,
49 fetch_latest_server_version: plugin.function("fetch_latest_server_version")?,
50 fetch_server_binary: plugin.function("fetch_server_binary")?,
51 cached_server_binary: plugin.function("cached_server_binary")?,
52 initialization_options: plugin.function("initialization_options")?,
53 language_ids: plugin.function("language_ids")?,
54 executor,
55 runtime: Arc::new(Mutex::new(plugin)),
56 })
57 }
58}
59
60#[async_trait]
61impl LspAdapter for PluginLspAdapter {
62 async fn name(&self) -> LanguageServerName {
63 let name: String = self
64 .runtime
65 .lock()
66 .await
67 .call(&self.name, ())
68 .await
69 .unwrap();
70 LanguageServerName(name.into())
71 }
72
73 fn short_name(&self) -> &'static str {
74 "PluginLspAdapter"
75 }
76
77 async fn fetch_latest_server_version(
78 &self,
79 _: &dyn LspAdapterDelegate,
80 ) -> Result<Box<dyn 'static + Send + Any>> {
81 let runtime = self.runtime.clone();
82 let function = self.fetch_latest_server_version;
83 self.executor
84 .spawn(async move {
85 let mut runtime = runtime.lock().await;
86 let versions: Result<Option<String>> =
87 runtime.call::<_, Option<String>>(&function, ()).await;
88 versions
89 .map_err(|e| anyhow!("{}", e))?
90 .ok_or_else(|| anyhow!("Could not fetch latest server version"))
91 .map(|v| Box::new(v) as Box<_>)
92 })
93 .await
94 }
95
96 async fn fetch_server_binary(
97 &self,
98 version: Box<dyn 'static + Send + Any>,
99 container_dir: PathBuf,
100 _: &dyn LspAdapterDelegate,
101 ) -> Result<LanguageServerBinary> {
102 let version = *version.downcast::<String>().unwrap();
103 let runtime = self.runtime.clone();
104 let function = self.fetch_server_binary;
105 self.executor
106 .spawn(async move {
107 let mut runtime = runtime.lock().await;
108 let handle = runtime.attach_path(&container_dir)?;
109 let result: Result<LanguageServerBinary, String> =
110 runtime.call(&function, (container_dir, version)).await?;
111 runtime.remove_resource(handle)?;
112 result.map_err(|e| anyhow!("{}", e))
113 })
114 .await
115 }
116
117 async fn cached_server_binary(
118 &self,
119 container_dir: PathBuf,
120 _: &dyn LspAdapterDelegate,
121 ) -> Option<LanguageServerBinary> {
122 let runtime = self.runtime.clone();
123 let function = self.cached_server_binary;
124
125 self.executor
126 .spawn(async move {
127 let mut runtime = runtime.lock().await;
128 let handle = runtime.attach_path(&container_dir).ok()?;
129 let result: Option<LanguageServerBinary> =
130 runtime.call(&function, container_dir).await.ok()?;
131 runtime.remove_resource(handle).ok()?;
132 result
133 })
134 .await
135 }
136
137 fn can_be_reinstalled(&self) -> bool {
138 false
139 }
140
141 async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
142 None
143 }
144
145 async fn initialization_options(&self) -> Option<serde_json::Value> {
146 let string: String = self
147 .runtime
148 .lock()
149 .await
150 .call(&self.initialization_options, ())
151 .await
152 .log_err()?;
153
154 serde_json::from_str(&string).ok()
155 }
156
157 async fn language_ids(&self) -> HashMap<String, String> {
158 self.runtime
159 .lock()
160 .await
161 .call(&self.language_ids, ())
162 .await
163 .log_err()
164 .unwrap_or_default()
165 .into_iter()
166 .collect()
167 }
168}