wit.rs

  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}