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