1mod since_v0_0_1;
2mod since_v0_0_4;
3mod since_v0_0_6;
4mod since_v0_1_0;
5mod since_v0_2_0;
6use indexed_docs::IndexedDocsDatabase;
7use release_channel::ReleaseChannel;
8use since_v0_2_0 as latest;
9
10use super::{wasm_engine, WasmState};
11use anyhow::{anyhow, Context, Result};
12use language::{LanguageServerName, LspAdapterDelegate};
13use semantic_version::SemanticVersion;
14use std::{ops::RangeInclusive, sync::Arc};
15use wasmtime::{
16 component::{Component, Instance, Linker, Resource},
17 Store,
18};
19
20#[cfg(test)]
21pub use latest::CodeLabelSpanLiteral;
22pub use latest::{
23 zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
24 zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
25 CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
26};
27pub use since_v0_0_4::LanguageServerConfig;
28
29pub fn new_linker(
30 f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
31) -> Linker<WasmState> {
32 let mut linker = Linker::new(&wasm_engine());
33 wasmtime_wasi::add_to_linker_async(&mut linker).unwrap();
34 f(&mut linker, wasi_view).unwrap();
35 linker
36}
37
38fn wasi_view(state: &mut WasmState) -> &mut WasmState {
39 state
40}
41
42/// Returns whether the given Wasm API version is supported by the Wasm host.
43pub fn is_supported_wasm_api_version(
44 release_channel: ReleaseChannel,
45 version: SemanticVersion,
46) -> bool {
47 wasm_api_version_range(release_channel).contains(&version)
48}
49
50/// Returns the Wasm API version range that is supported by the Wasm host.
51#[inline(always)]
52pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
53 // Note: The release channel can be used to stage a new version of the extension API.
54 let _ = release_channel;
55
56 let max_version = match release_channel {
57 ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
58 ReleaseChannel::Stable | ReleaseChannel::Preview => since_v0_1_0::MAX_VERSION,
59 };
60
61 since_v0_0_1::MIN_VERSION..=max_version
62}
63
64pub enum Extension {
65 V020(since_v0_2_0::Extension),
66 V010(since_v0_1_0::Extension),
67 V006(since_v0_0_6::Extension),
68 V004(since_v0_0_4::Extension),
69 V001(since_v0_0_1::Extension),
70}
71
72impl Extension {
73 pub async fn instantiate_async(
74 store: &mut Store<WasmState>,
75 release_channel: ReleaseChannel,
76 version: SemanticVersion,
77 component: &Component,
78 ) -> Result<(Self, Instance)> {
79 // Note: The release channel can be used to stage a new version of the extension API.
80 let _ = release_channel;
81
82 let allow_latest_version = match release_channel {
83 ReleaseChannel::Dev | ReleaseChannel::Nightly => true,
84 ReleaseChannel::Stable | ReleaseChannel::Preview => false,
85 };
86
87 if allow_latest_version && version >= latest::MIN_VERSION {
88 let (extension, instance) =
89 latest::Extension::instantiate_async(store, component, latest::linker())
90 .await
91 .context("failed to instantiate wasm extension")?;
92 Ok((Self::V020(extension), instance))
93 } else if version >= since_v0_1_0::MIN_VERSION {
94 let (extension, instance) = since_v0_1_0::Extension::instantiate_async(
95 store,
96 component,
97 since_v0_1_0::linker(),
98 )
99 .await
100 .context("failed to instantiate wasm extension")?;
101 Ok((Self::V010(extension), instance))
102 } else if version >= since_v0_0_6::MIN_VERSION {
103 let (extension, instance) = since_v0_0_6::Extension::instantiate_async(
104 store,
105 component,
106 since_v0_0_6::linker(),
107 )
108 .await
109 .context("failed to instantiate wasm extension")?;
110 Ok((Self::V006(extension), instance))
111 } else if version >= since_v0_0_4::MIN_VERSION {
112 let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
113 store,
114 component,
115 since_v0_0_4::linker(),
116 )
117 .await
118 .context("failed to instantiate wasm extension")?;
119 Ok((Self::V004(extension), instance))
120 } else {
121 let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
122 store,
123 component,
124 since_v0_0_1::linker(),
125 )
126 .await
127 .context("failed to instantiate wasm extension")?;
128 Ok((Self::V001(extension), instance))
129 }
130 }
131
132 pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
133 match self {
134 Extension::V020(ext) => ext.call_init_extension(store).await,
135 Extension::V010(ext) => ext.call_init_extension(store).await,
136 Extension::V006(ext) => ext.call_init_extension(store).await,
137 Extension::V004(ext) => ext.call_init_extension(store).await,
138 Extension::V001(ext) => ext.call_init_extension(store).await,
139 }
140 }
141
142 pub async fn call_language_server_command(
143 &self,
144 store: &mut Store<WasmState>,
145 language_server_id: &LanguageServerName,
146 config: &LanguageServerConfig,
147 resource: Resource<Arc<dyn LspAdapterDelegate>>,
148 ) -> Result<Result<Command, String>> {
149 match self {
150 Extension::V020(ext) => {
151 ext.call_language_server_command(store, &language_server_id.0, resource)
152 .await
153 }
154 Extension::V010(ext) => Ok(ext
155 .call_language_server_command(store, &language_server_id.0, resource)
156 .await?
157 .map(|command| command.into())),
158 Extension::V006(ext) => Ok(ext
159 .call_language_server_command(store, &language_server_id.0, resource)
160 .await?
161 .map(|command| command.into())),
162 Extension::V004(ext) => Ok(ext
163 .call_language_server_command(store, config, resource)
164 .await?
165 .map(|command| command.into())),
166 Extension::V001(ext) => Ok(ext
167 .call_language_server_command(store, &config.clone().into(), resource)
168 .await?
169 .map(|command| command.into())),
170 }
171 }
172
173 pub async fn call_language_server_initialization_options(
174 &self,
175 store: &mut Store<WasmState>,
176 language_server_id: &LanguageServerName,
177 config: &LanguageServerConfig,
178 resource: Resource<Arc<dyn LspAdapterDelegate>>,
179 ) -> Result<Result<Option<String>, String>> {
180 match self {
181 Extension::V020(ext) => {
182 ext.call_language_server_initialization_options(
183 store,
184 &language_server_id.0,
185 resource,
186 )
187 .await
188 }
189 Extension::V010(ext) => {
190 ext.call_language_server_initialization_options(
191 store,
192 &language_server_id.0,
193 resource,
194 )
195 .await
196 }
197 Extension::V006(ext) => {
198 ext.call_language_server_initialization_options(
199 store,
200 &language_server_id.0,
201 resource,
202 )
203 .await
204 }
205 Extension::V004(ext) => {
206 ext.call_language_server_initialization_options(store, config, resource)
207 .await
208 }
209 Extension::V001(ext) => {
210 ext.call_language_server_initialization_options(
211 store,
212 &config.clone().into(),
213 resource,
214 )
215 .await
216 }
217 }
218 }
219
220 pub async fn call_language_server_workspace_configuration(
221 &self,
222 store: &mut Store<WasmState>,
223 language_server_id: &LanguageServerName,
224 resource: Resource<Arc<dyn LspAdapterDelegate>>,
225 ) -> Result<Result<Option<String>, String>> {
226 match self {
227 Extension::V020(ext) => {
228 ext.call_language_server_workspace_configuration(
229 store,
230 &language_server_id.0,
231 resource,
232 )
233 .await
234 }
235 Extension::V010(ext) => {
236 ext.call_language_server_workspace_configuration(
237 store,
238 &language_server_id.0,
239 resource,
240 )
241 .await
242 }
243 Extension::V006(ext) => {
244 ext.call_language_server_workspace_configuration(
245 store,
246 &language_server_id.0,
247 resource,
248 )
249 .await
250 }
251 Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
252 }
253 }
254
255 pub async fn call_labels_for_completions(
256 &self,
257 store: &mut Store<WasmState>,
258 language_server_id: &LanguageServerName,
259 completions: Vec<latest::Completion>,
260 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
261 match self {
262 Extension::V020(ext) => {
263 ext.call_labels_for_completions(store, &language_server_id.0, &completions)
264 .await
265 }
266 Extension::V010(ext) => Ok(ext
267 .call_labels_for_completions(store, &language_server_id.0, &completions)
268 .await?
269 .map(|labels| {
270 labels
271 .into_iter()
272 .map(|label| label.map(Into::into))
273 .collect()
274 })),
275 Extension::V006(ext) => Ok(ext
276 .call_labels_for_completions(store, &language_server_id.0, &completions)
277 .await?
278 .map(|labels| {
279 labels
280 .into_iter()
281 .map(|label| label.map(Into::into))
282 .collect()
283 })),
284 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
285 }
286 }
287
288 pub async fn call_labels_for_symbols(
289 &self,
290 store: &mut Store<WasmState>,
291 language_server_id: &LanguageServerName,
292 symbols: Vec<latest::Symbol>,
293 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
294 match self {
295 Extension::V020(ext) => {
296 ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
297 .await
298 }
299 Extension::V010(ext) => Ok(ext
300 .call_labels_for_symbols(store, &language_server_id.0, &symbols)
301 .await?
302 .map(|labels| {
303 labels
304 .into_iter()
305 .map(|label| label.map(Into::into))
306 .collect()
307 })),
308 Extension::V006(ext) => Ok(ext
309 .call_labels_for_symbols(store, &language_server_id.0, &symbols)
310 .await?
311 .map(|labels| {
312 labels
313 .into_iter()
314 .map(|label| label.map(Into::into))
315 .collect()
316 })),
317 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
318 }
319 }
320
321 pub async fn call_complete_slash_command_argument(
322 &self,
323 store: &mut Store<WasmState>,
324 command: &SlashCommand,
325 arguments: &[String],
326 ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
327 match self {
328 Extension::V020(ext) => {
329 ext.call_complete_slash_command_argument(store, command, arguments)
330 .await
331 }
332 Extension::V010(ext) => {
333 ext.call_complete_slash_command_argument(store, command, arguments)
334 .await
335 }
336 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
337 }
338 }
339
340 pub async fn call_run_slash_command(
341 &self,
342 store: &mut Store<WasmState>,
343 command: &SlashCommand,
344 arguments: &[String],
345 resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
346 ) -> Result<Result<SlashCommandOutput, String>> {
347 match self {
348 Extension::V020(ext) => {
349 ext.call_run_slash_command(store, command, arguments, resource)
350 .await
351 }
352 Extension::V010(ext) => {
353 ext.call_run_slash_command(store, command, arguments, resource)
354 .await
355 }
356 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
357 Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
358 }
359 }
360 }
361
362 pub async fn call_suggest_docs_packages(
363 &self,
364 store: &mut Store<WasmState>,
365 provider: &str,
366 ) -> Result<Result<Vec<String>, String>> {
367 match self {
368 Extension::V020(ext) => ext.call_suggest_docs_packages(store, provider).await,
369 Extension::V010(ext) => ext.call_suggest_docs_packages(store, provider).await,
370 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Err(anyhow!(
371 "`suggest_docs_packages` not available prior to v0.1.0"
372 )),
373 }
374 }
375
376 pub async fn call_index_docs(
377 &self,
378 store: &mut Store<WasmState>,
379 provider: &str,
380 package_name: &str,
381 database: Resource<Arc<IndexedDocsDatabase>>,
382 ) -> Result<Result<(), String>> {
383 match self {
384 Extension::V020(ext) => {
385 ext.call_index_docs(store, provider, package_name, database)
386 .await
387 }
388 Extension::V010(ext) => {
389 ext.call_index_docs(store, provider, package_name, database)
390 .await
391 }
392 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
393 Err(anyhow!("`index_docs` not available prior to v0.1.0"))
394 }
395 }
396 }
397}
398
399trait ToWasmtimeResult<T> {
400 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
401}
402
403impl<T> ToWasmtimeResult<T> for Result<T> {
404 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
405 Ok(self.map_err(|error| error.to_string()))
406 }
407}