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;
6mod since_v0_3_0;
7use extension::{KeyValueStoreDelegate, WorktreeDelegate};
8use language::LanguageName;
9use lsp::LanguageServerName;
10use release_channel::ReleaseChannel;
11use since_v0_3_0 as latest;
12
13use super::{wasm_engine, WasmState};
14use anyhow::{anyhow, Context as _, Result};
15use semantic_version::SemanticVersion;
16use std::{ops::RangeInclusive, sync::Arc};
17use wasmtime::{
18 component::{Component, Linker, Resource},
19 Store,
20};
21
22#[cfg(test)]
23pub use latest::CodeLabelSpanLiteral;
24pub use latest::{
25 zed::extension::lsp::{
26 Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
27 },
28 zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
29 CodeLabel, CodeLabelSpan, Command, ExtensionProject, Range, SlashCommand,
30};
31pub use since_v0_0_4::LanguageServerConfig;
32
33pub fn new_linker(
34 f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
35) -> Linker<WasmState> {
36 let mut linker = Linker::new(&wasm_engine());
37 wasmtime_wasi::add_to_linker_async(&mut linker).unwrap();
38 f(&mut linker, wasi_view).unwrap();
39 linker
40}
41
42fn wasi_view(state: &mut WasmState) -> &mut WasmState {
43 state
44}
45
46/// Returns whether the given Wasm API version is supported by the Wasm host.
47pub fn is_supported_wasm_api_version(
48 release_channel: ReleaseChannel,
49 version: SemanticVersion,
50) -> bool {
51 wasm_api_version_range(release_channel).contains(&version)
52}
53
54/// Returns the Wasm API version range that is supported by the Wasm host.
55#[inline(always)]
56pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
57 // Note: The release channel can be used to stage a new version of the extension API.
58 let _ = release_channel;
59
60 let max_version = match release_channel {
61 ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
62 ReleaseChannel::Stable | ReleaseChannel::Preview => latest::MAX_VERSION,
63 };
64
65 since_v0_0_1::MIN_VERSION..=max_version
66}
67
68/// Authorizes access to use unreleased versions of the Wasm API, based on the provided [`ReleaseChannel`].
69///
70/// Note: If there isn't currently an unreleased Wasm API version this function may be unused. Don't delete it!
71pub fn authorize_access_to_unreleased_wasm_api_version(
72 release_channel: ReleaseChannel,
73) -> Result<()> {
74 let allow_unreleased_version = match release_channel {
75 ReleaseChannel::Dev | ReleaseChannel::Nightly => true,
76 ReleaseChannel::Stable | ReleaseChannel::Preview => {
77 // We always allow the latest in tests so that the extension tests pass on release branches.
78 cfg!(any(test, feature = "test-support"))
79 }
80 };
81
82 if !allow_unreleased_version {
83 Err(anyhow!(
84 "unreleased versions of the extension API can only be used on development builds of Zed"
85 ))?;
86 }
87
88 Ok(())
89}
90
91pub enum Extension {
92 V030(since_v0_3_0::Extension),
93 V020(since_v0_2_0::Extension),
94 V010(since_v0_1_0::Extension),
95 V006(since_v0_0_6::Extension),
96 V004(since_v0_0_4::Extension),
97 V001(since_v0_0_1::Extension),
98}
99
100impl Extension {
101 pub async fn instantiate_async(
102 store: &mut Store<WasmState>,
103 release_channel: ReleaseChannel,
104 version: SemanticVersion,
105 component: &Component,
106 ) -> Result<Self> {
107 // Note: The release channel can be used to stage a new version of the extension API.
108 let _ = release_channel;
109
110 if version >= latest::MIN_VERSION {
111 let extension =
112 latest::Extension::instantiate_async(store, component, latest::linker())
113 .await
114 .context("failed to instantiate wasm extension")?;
115 Ok(Self::V030(extension))
116 } else if version >= since_v0_2_0::MIN_VERSION {
117 let extension = since_v0_2_0::Extension::instantiate_async(
118 store,
119 component,
120 since_v0_2_0::linker(),
121 )
122 .await
123 .context("failed to instantiate wasm extension")?;
124 Ok(Self::V020(extension))
125 } else if version >= since_v0_1_0::MIN_VERSION {
126 let extension = since_v0_1_0::Extension::instantiate_async(
127 store,
128 component,
129 since_v0_1_0::linker(),
130 )
131 .await
132 .context("failed to instantiate wasm extension")?;
133 Ok(Self::V010(extension))
134 } else if version >= since_v0_0_6::MIN_VERSION {
135 let extension = since_v0_0_6::Extension::instantiate_async(
136 store,
137 component,
138 since_v0_0_6::linker(),
139 )
140 .await
141 .context("failed to instantiate wasm extension")?;
142 Ok(Self::V006(extension))
143 } else if version >= since_v0_0_4::MIN_VERSION {
144 let extension = since_v0_0_4::Extension::instantiate_async(
145 store,
146 component,
147 since_v0_0_4::linker(),
148 )
149 .await
150 .context("failed to instantiate wasm extension")?;
151 Ok(Self::V004(extension))
152 } else {
153 let extension = since_v0_0_1::Extension::instantiate_async(
154 store,
155 component,
156 since_v0_0_1::linker(),
157 )
158 .await
159 .context("failed to instantiate wasm extension")?;
160 Ok(Self::V001(extension))
161 }
162 }
163
164 pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
165 match self {
166 Extension::V030(ext) => ext.call_init_extension(store).await,
167 Extension::V020(ext) => ext.call_init_extension(store).await,
168 Extension::V010(ext) => ext.call_init_extension(store).await,
169 Extension::V006(ext) => ext.call_init_extension(store).await,
170 Extension::V004(ext) => ext.call_init_extension(store).await,
171 Extension::V001(ext) => ext.call_init_extension(store).await,
172 }
173 }
174
175 pub async fn call_language_server_command(
176 &self,
177 store: &mut Store<WasmState>,
178 language_server_id: &LanguageServerName,
179 language_name: &LanguageName,
180 resource: Resource<Arc<dyn WorktreeDelegate>>,
181 ) -> Result<Result<Command, String>> {
182 match self {
183 Extension::V030(ext) => {
184 ext.call_language_server_command(store, &language_server_id.0, resource)
185 .await
186 }
187 Extension::V020(ext) => Ok(ext
188 .call_language_server_command(store, &language_server_id.0, resource)
189 .await?
190 .map(|command| command.into())),
191 Extension::V010(ext) => Ok(ext
192 .call_language_server_command(store, &language_server_id.0, resource)
193 .await?
194 .map(|command| command.into())),
195 Extension::V006(ext) => Ok(ext
196 .call_language_server_command(store, &language_server_id.0, resource)
197 .await?
198 .map(|command| command.into())),
199 Extension::V004(ext) => Ok(ext
200 .call_language_server_command(
201 store,
202 &LanguageServerConfig {
203 name: language_server_id.0.to_string(),
204 language_name: language_name.to_string(),
205 },
206 resource,
207 )
208 .await?
209 .map(|command| command.into())),
210 Extension::V001(ext) => Ok(ext
211 .call_language_server_command(
212 store,
213 &LanguageServerConfig {
214 name: language_server_id.0.to_string(),
215 language_name: language_name.to_string(),
216 }
217 .into(),
218 resource,
219 )
220 .await?
221 .map(|command| command.into())),
222 }
223 }
224
225 pub async fn call_language_server_initialization_options(
226 &self,
227 store: &mut Store<WasmState>,
228 language_server_id: &LanguageServerName,
229 language_name: &LanguageName,
230 resource: Resource<Arc<dyn WorktreeDelegate>>,
231 ) -> Result<Result<Option<String>, String>> {
232 match self {
233 Extension::V030(ext) => {
234 ext.call_language_server_initialization_options(
235 store,
236 &language_server_id.0,
237 resource,
238 )
239 .await
240 }
241 Extension::V020(ext) => {
242 ext.call_language_server_initialization_options(
243 store,
244 &language_server_id.0,
245 resource,
246 )
247 .await
248 }
249 Extension::V010(ext) => {
250 ext.call_language_server_initialization_options(
251 store,
252 &language_server_id.0,
253 resource,
254 )
255 .await
256 }
257 Extension::V006(ext) => {
258 ext.call_language_server_initialization_options(
259 store,
260 &language_server_id.0,
261 resource,
262 )
263 .await
264 }
265 Extension::V004(ext) => {
266 ext.call_language_server_initialization_options(
267 store,
268 &LanguageServerConfig {
269 name: language_server_id.0.to_string(),
270 language_name: language_name.to_string(),
271 },
272 resource,
273 )
274 .await
275 }
276 Extension::V001(ext) => {
277 ext.call_language_server_initialization_options(
278 store,
279 &LanguageServerConfig {
280 name: language_server_id.0.to_string(),
281 language_name: language_name.to_string(),
282 }
283 .into(),
284 resource,
285 )
286 .await
287 }
288 }
289 }
290
291 pub async fn call_language_server_workspace_configuration(
292 &self,
293 store: &mut Store<WasmState>,
294 language_server_id: &LanguageServerName,
295 resource: Resource<Arc<dyn WorktreeDelegate>>,
296 ) -> Result<Result<Option<String>, String>> {
297 match self {
298 Extension::V030(ext) => {
299 ext.call_language_server_workspace_configuration(
300 store,
301 &language_server_id.0,
302 resource,
303 )
304 .await
305 }
306 Extension::V020(ext) => {
307 ext.call_language_server_workspace_configuration(
308 store,
309 &language_server_id.0,
310 resource,
311 )
312 .await
313 }
314 Extension::V010(ext) => {
315 ext.call_language_server_workspace_configuration(
316 store,
317 &language_server_id.0,
318 resource,
319 )
320 .await
321 }
322 Extension::V006(ext) => {
323 ext.call_language_server_workspace_configuration(
324 store,
325 &language_server_id.0,
326 resource,
327 )
328 .await
329 }
330 Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
331 }
332 }
333
334 pub async fn call_labels_for_completions(
335 &self,
336 store: &mut Store<WasmState>,
337 language_server_id: &LanguageServerName,
338 completions: Vec<latest::Completion>,
339 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
340 match self {
341 Extension::V030(ext) => {
342 ext.call_labels_for_completions(store, &language_server_id.0, &completions)
343 .await
344 }
345 Extension::V020(ext) => Ok(ext
346 .call_labels_for_completions(
347 store,
348 &language_server_id.0,
349 &completions.into_iter().collect::<Vec<_>>(),
350 )
351 .await?
352 .map(|labels| {
353 labels
354 .into_iter()
355 .map(|label| label.map(Into::into))
356 .collect()
357 })),
358 Extension::V010(ext) => Ok(ext
359 .call_labels_for_completions(
360 store,
361 &language_server_id.0,
362 &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
363 )
364 .await?
365 .map(|labels| {
366 labels
367 .into_iter()
368 .map(|label| label.map(Into::into))
369 .collect()
370 })),
371 Extension::V006(ext) => Ok(ext
372 .call_labels_for_completions(
373 store,
374 &language_server_id.0,
375 &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
376 )
377 .await?
378 .map(|labels| {
379 labels
380 .into_iter()
381 .map(|label| label.map(Into::into))
382 .collect()
383 })),
384 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
385 }
386 }
387
388 pub async fn call_labels_for_symbols(
389 &self,
390 store: &mut Store<WasmState>,
391 language_server_id: &LanguageServerName,
392 symbols: Vec<latest::Symbol>,
393 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
394 match self {
395 Extension::V030(ext) => {
396 ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
397 .await
398 }
399 Extension::V020(ext) => Ok(ext
400 .call_labels_for_symbols(
401 store,
402 &language_server_id.0,
403 &symbols.into_iter().collect::<Vec<_>>(),
404 )
405 .await?
406 .map(|labels| {
407 labels
408 .into_iter()
409 .map(|label| label.map(Into::into))
410 .collect()
411 })),
412 Extension::V010(ext) => Ok(ext
413 .call_labels_for_symbols(
414 store,
415 &language_server_id.0,
416 &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
417 )
418 .await?
419 .map(|labels| {
420 labels
421 .into_iter()
422 .map(|label| label.map(Into::into))
423 .collect()
424 })),
425 Extension::V006(ext) => Ok(ext
426 .call_labels_for_symbols(
427 store,
428 &language_server_id.0,
429 &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
430 )
431 .await?
432 .map(|labels| {
433 labels
434 .into_iter()
435 .map(|label| label.map(Into::into))
436 .collect()
437 })),
438 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
439 }
440 }
441
442 pub async fn call_complete_slash_command_argument(
443 &self,
444 store: &mut Store<WasmState>,
445 command: &SlashCommand,
446 arguments: &[String],
447 ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
448 match self {
449 Extension::V030(ext) => {
450 ext.call_complete_slash_command_argument(store, command, arguments)
451 .await
452 }
453 Extension::V020(ext) => {
454 ext.call_complete_slash_command_argument(store, command, arguments)
455 .await
456 }
457 Extension::V010(ext) => {
458 ext.call_complete_slash_command_argument(store, command, arguments)
459 .await
460 }
461 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
462 }
463 }
464
465 pub async fn call_run_slash_command(
466 &self,
467 store: &mut Store<WasmState>,
468 command: &SlashCommand,
469 arguments: &[String],
470 resource: Option<Resource<Arc<dyn WorktreeDelegate>>>,
471 ) -> Result<Result<SlashCommandOutput, String>> {
472 match self {
473 Extension::V030(ext) => {
474 ext.call_run_slash_command(store, command, arguments, resource)
475 .await
476 }
477 Extension::V020(ext) => {
478 ext.call_run_slash_command(store, command, arguments, resource)
479 .await
480 }
481 Extension::V010(ext) => {
482 ext.call_run_slash_command(store, command, arguments, resource)
483 .await
484 }
485 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
486 Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
487 }
488 }
489 }
490
491 pub async fn call_context_server_command(
492 &self,
493 store: &mut Store<WasmState>,
494 context_server_id: Arc<str>,
495 project: Resource<ExtensionProject>,
496 ) -> Result<Result<Command, String>> {
497 match self {
498 Extension::V030(ext) => {
499 ext.call_context_server_command(store, &context_server_id, project)
500 .await
501 }
502 Extension::V020(ext) => Ok(ext
503 .call_context_server_command(store, &context_server_id, project)
504 .await?
505 .map(Into::into)),
506 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) | Extension::V010(_) => {
507 Err(anyhow!(
508 "`context_server_command` not available prior to v0.2.0"
509 ))
510 }
511 }
512 }
513
514 pub async fn call_suggest_docs_packages(
515 &self,
516 store: &mut Store<WasmState>,
517 provider: &str,
518 ) -> Result<Result<Vec<String>, String>> {
519 match self {
520 Extension::V030(ext) => ext.call_suggest_docs_packages(store, provider).await,
521 Extension::V020(ext) => ext.call_suggest_docs_packages(store, provider).await,
522 Extension::V010(ext) => ext.call_suggest_docs_packages(store, provider).await,
523 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Err(anyhow!(
524 "`suggest_docs_packages` not available prior to v0.1.0"
525 )),
526 }
527 }
528
529 pub async fn call_index_docs(
530 &self,
531 store: &mut Store<WasmState>,
532 provider: &str,
533 package_name: &str,
534 kv_store: Resource<Arc<dyn KeyValueStoreDelegate>>,
535 ) -> Result<Result<(), String>> {
536 match self {
537 Extension::V030(ext) => {
538 ext.call_index_docs(store, provider, package_name, kv_store)
539 .await
540 }
541 Extension::V020(ext) => {
542 ext.call_index_docs(store, provider, package_name, kv_store)
543 .await
544 }
545 Extension::V010(ext) => {
546 ext.call_index_docs(store, provider, package_name, kv_store)
547 .await
548 }
549 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
550 Err(anyhow!("`index_docs` not available prior to v0.1.0"))
551 }
552 }
553 }
554}
555
556trait ToWasmtimeResult<T> {
557 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
558}
559
560impl<T> ToWasmtimeResult<T> for Result<T> {
561 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
562 Ok(self.map_err(|error| error.to_string()))
563 }
564}