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 => since_v0_3_0::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            authorize_access_to_unreleased_wasm_api_version(release_channel)?;
114
115            let extension =
116                latest::Extension::instantiate_async(store, component, latest::linker())
117                    .await
118                    .context("failed to instantiate wasm extension")?;
119            Ok(Self::V0_4_0(extension))
120        } else if version >= since_v0_3_0::MIN_VERSION {
121            let extension = since_v0_3_0::Extension::instantiate_async(
122                store,
123                component,
124                since_v0_3_0::linker(),
125            )
126            .await
127            .context("failed to instantiate wasm extension")?;
128            Ok(Self::V0_3_0(extension))
129        } else if version >= since_v0_2_0::MIN_VERSION {
130            let extension = since_v0_2_0::Extension::instantiate_async(
131                store,
132                component,
133                since_v0_2_0::linker(),
134            )
135            .await
136            .context("failed to instantiate wasm extension")?;
137            Ok(Self::V0_2_0(extension))
138        } else if version >= since_v0_1_0::MIN_VERSION {
139            let extension = since_v0_1_0::Extension::instantiate_async(
140                store,
141                component,
142                since_v0_1_0::linker(),
143            )
144            .await
145            .context("failed to instantiate wasm extension")?;
146            Ok(Self::V0_1_0(extension))
147        } else if version >= since_v0_0_6::MIN_VERSION {
148            let extension = since_v0_0_6::Extension::instantiate_async(
149                store,
150                component,
151                since_v0_0_6::linker(),
152            )
153            .await
154            .context("failed to instantiate wasm extension")?;
155            Ok(Self::V0_0_6(extension))
156        } else if version >= since_v0_0_4::MIN_VERSION {
157            let extension = since_v0_0_4::Extension::instantiate_async(
158                store,
159                component,
160                since_v0_0_4::linker(),
161            )
162            .await
163            .context("failed to instantiate wasm extension")?;
164            Ok(Self::V0_0_4(extension))
165        } else {
166            let extension = since_v0_0_1::Extension::instantiate_async(
167                store,
168                component,
169                since_v0_0_1::linker(),
170            )
171            .await
172            .context("failed to instantiate wasm extension")?;
173            Ok(Self::V0_0_1(extension))
174        }
175    }
176
177    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
178        match self {
179            Extension::V0_4_0(ext) => ext.call_init_extension(store).await,
180            Extension::V0_3_0(ext) => ext.call_init_extension(store).await,
181            Extension::V0_2_0(ext) => ext.call_init_extension(store).await,
182            Extension::V0_1_0(ext) => ext.call_init_extension(store).await,
183            Extension::V0_0_6(ext) => ext.call_init_extension(store).await,
184            Extension::V0_0_4(ext) => ext.call_init_extension(store).await,
185            Extension::V0_0_1(ext) => ext.call_init_extension(store).await,
186        }
187    }
188
189    pub async fn call_language_server_command(
190        &self,
191        store: &mut Store<WasmState>,
192        language_server_id: &LanguageServerName,
193        language_name: &LanguageName,
194        resource: Resource<Arc<dyn WorktreeDelegate>>,
195    ) -> Result<Result<Command, String>> {
196        match self {
197            Extension::V0_4_0(ext) => {
198                ext.call_language_server_command(store, &language_server_id.0, resource)
199                    .await
200            }
201            Extension::V0_3_0(ext) => {
202                ext.call_language_server_command(store, &language_server_id.0, resource)
203                    .await
204            }
205            Extension::V0_2_0(ext) => Ok(ext
206                .call_language_server_command(store, &language_server_id.0, resource)
207                .await?
208                .map(|command| command.into())),
209            Extension::V0_1_0(ext) => Ok(ext
210                .call_language_server_command(store, &language_server_id.0, resource)
211                .await?
212                .map(|command| command.into())),
213            Extension::V0_0_6(ext) => Ok(ext
214                .call_language_server_command(store, &language_server_id.0, resource)
215                .await?
216                .map(|command| command.into())),
217            Extension::V0_0_4(ext) => Ok(ext
218                .call_language_server_command(
219                    store,
220                    &LanguageServerConfig {
221                        name: language_server_id.0.to_string(),
222                        language_name: language_name.to_string(),
223                    },
224                    resource,
225                )
226                .await?
227                .map(|command| command.into())),
228            Extension::V0_0_1(ext) => Ok(ext
229                .call_language_server_command(
230                    store,
231                    &LanguageServerConfig {
232                        name: language_server_id.0.to_string(),
233                        language_name: language_name.to_string(),
234                    }
235                    .into(),
236                    resource,
237                )
238                .await?
239                .map(|command| command.into())),
240        }
241    }
242
243    pub async fn call_language_server_initialization_options(
244        &self,
245        store: &mut Store<WasmState>,
246        language_server_id: &LanguageServerName,
247        language_name: &LanguageName,
248        resource: Resource<Arc<dyn WorktreeDelegate>>,
249    ) -> Result<Result<Option<String>, String>> {
250        match self {
251            Extension::V0_4_0(ext) => {
252                ext.call_language_server_initialization_options(
253                    store,
254                    &language_server_id.0,
255                    resource,
256                )
257                .await
258            }
259            Extension::V0_3_0(ext) => {
260                ext.call_language_server_initialization_options(
261                    store,
262                    &language_server_id.0,
263                    resource,
264                )
265                .await
266            }
267            Extension::V0_2_0(ext) => {
268                ext.call_language_server_initialization_options(
269                    store,
270                    &language_server_id.0,
271                    resource,
272                )
273                .await
274            }
275            Extension::V0_1_0(ext) => {
276                ext.call_language_server_initialization_options(
277                    store,
278                    &language_server_id.0,
279                    resource,
280                )
281                .await
282            }
283            Extension::V0_0_6(ext) => {
284                ext.call_language_server_initialization_options(
285                    store,
286                    &language_server_id.0,
287                    resource,
288                )
289                .await
290            }
291            Extension::V0_0_4(ext) => {
292                ext.call_language_server_initialization_options(
293                    store,
294                    &LanguageServerConfig {
295                        name: language_server_id.0.to_string(),
296                        language_name: language_name.to_string(),
297                    },
298                    resource,
299                )
300                .await
301            }
302            Extension::V0_0_1(ext) => {
303                ext.call_language_server_initialization_options(
304                    store,
305                    &LanguageServerConfig {
306                        name: language_server_id.0.to_string(),
307                        language_name: language_name.to_string(),
308                    }
309                    .into(),
310                    resource,
311                )
312                .await
313            }
314        }
315    }
316
317    pub async fn call_language_server_workspace_configuration(
318        &self,
319        store: &mut Store<WasmState>,
320        language_server_id: &LanguageServerName,
321        resource: Resource<Arc<dyn WorktreeDelegate>>,
322    ) -> Result<Result<Option<String>, String>> {
323        match self {
324            Extension::V0_4_0(ext) => {
325                ext.call_language_server_workspace_configuration(
326                    store,
327                    &language_server_id.0,
328                    resource,
329                )
330                .await
331            }
332            Extension::V0_3_0(ext) => {
333                ext.call_language_server_workspace_configuration(
334                    store,
335                    &language_server_id.0,
336                    resource,
337                )
338                .await
339            }
340            Extension::V0_2_0(ext) => {
341                ext.call_language_server_workspace_configuration(
342                    store,
343                    &language_server_id.0,
344                    resource,
345                )
346                .await
347            }
348            Extension::V0_1_0(ext) => {
349                ext.call_language_server_workspace_configuration(
350                    store,
351                    &language_server_id.0,
352                    resource,
353                )
354                .await
355            }
356            Extension::V0_0_6(ext) => {
357                ext.call_language_server_workspace_configuration(
358                    store,
359                    &language_server_id.0,
360                    resource,
361                )
362                .await
363            }
364            Extension::V0_0_4(_) | Extension::V0_0_1(_) => Ok(Ok(None)),
365        }
366    }
367
368    pub async fn call_language_server_additional_initialization_options(
369        &self,
370        store: &mut Store<WasmState>,
371        language_server_id: &LanguageServerName,
372        target_language_server_id: &LanguageServerName,
373        resource: Resource<Arc<dyn WorktreeDelegate>>,
374    ) -> Result<Result<Option<String>, String>> {
375        match self {
376            Extension::V0_4_0(ext) => {
377                ext.call_language_server_additional_initialization_options(
378                    store,
379                    &language_server_id.0,
380                    &target_language_server_id.0,
381                    resource,
382                )
383                .await
384            }
385            Extension::V0_3_0(_)
386            | Extension::V0_2_0(_)
387            | Extension::V0_1_0(_)
388            | Extension::V0_0_6(_)
389            | Extension::V0_0_4(_)
390            | Extension::V0_0_1(_) => Ok(Ok(None)),
391        }
392    }
393
394    pub async fn call_language_server_additional_workspace_configuration(
395        &self,
396        store: &mut Store<WasmState>,
397        language_server_id: &LanguageServerName,
398        target_language_server_id: &LanguageServerName,
399        resource: Resource<Arc<dyn WorktreeDelegate>>,
400    ) -> Result<Result<Option<String>, String>> {
401        match self {
402            Extension::V0_4_0(ext) => {
403                ext.call_language_server_additional_workspace_configuration(
404                    store,
405                    &language_server_id.0,
406                    &target_language_server_id.0,
407                    resource,
408                )
409                .await
410            }
411            Extension::V0_3_0(_)
412            | Extension::V0_2_0(_)
413            | Extension::V0_1_0(_)
414            | Extension::V0_0_6(_)
415            | Extension::V0_0_4(_)
416            | Extension::V0_0_1(_) => Ok(Ok(None)),
417        }
418    }
419
420    pub async fn call_labels_for_completions(
421        &self,
422        store: &mut Store<WasmState>,
423        language_server_id: &LanguageServerName,
424        completions: Vec<latest::Completion>,
425    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
426        match self {
427            Extension::V0_4_0(ext) => {
428                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
429                    .await
430            }
431            Extension::V0_3_0(ext) => Ok(ext
432                .call_labels_for_completions(
433                    store,
434                    &language_server_id.0,
435                    &completions.into_iter().collect::<Vec<_>>(),
436                )
437                .await?
438                .map(|labels| {
439                    labels
440                        .into_iter()
441                        .map(|label| label.map(Into::into))
442                        .collect()
443                })),
444            Extension::V0_2_0(ext) => Ok(ext
445                .call_labels_for_completions(
446                    store,
447                    &language_server_id.0,
448                    &completions.into_iter().collect::<Vec<_>>(),
449                )
450                .await?
451                .map(|labels| {
452                    labels
453                        .into_iter()
454                        .map(|label| label.map(Into::into))
455                        .collect()
456                })),
457            Extension::V0_1_0(ext) => Ok(ext
458                .call_labels_for_completions(
459                    store,
460                    &language_server_id.0,
461                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
462                )
463                .await?
464                .map(|labels| {
465                    labels
466                        .into_iter()
467                        .map(|label| label.map(Into::into))
468                        .collect()
469                })),
470            Extension::V0_0_6(ext) => Ok(ext
471                .call_labels_for_completions(
472                    store,
473                    &language_server_id.0,
474                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
475                )
476                .await?
477                .map(|labels| {
478                    labels
479                        .into_iter()
480                        .map(|label| label.map(Into::into))
481                        .collect()
482                })),
483            Extension::V0_0_1(_) | Extension::V0_0_4(_) => Ok(Ok(Vec::new())),
484        }
485    }
486
487    pub async fn call_labels_for_symbols(
488        &self,
489        store: &mut Store<WasmState>,
490        language_server_id: &LanguageServerName,
491        symbols: Vec<latest::Symbol>,
492    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
493        match self {
494            Extension::V0_4_0(ext) => {
495                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
496                    .await
497            }
498            Extension::V0_3_0(ext) => Ok(ext
499                .call_labels_for_symbols(
500                    store,
501                    &language_server_id.0,
502                    &symbols.into_iter().collect::<Vec<_>>(),
503                )
504                .await?
505                .map(|labels| {
506                    labels
507                        .into_iter()
508                        .map(|label| label.map(Into::into))
509                        .collect()
510                })),
511            Extension::V0_2_0(ext) => Ok(ext
512                .call_labels_for_symbols(
513                    store,
514                    &language_server_id.0,
515                    &symbols.into_iter().collect::<Vec<_>>(),
516                )
517                .await?
518                .map(|labels| {
519                    labels
520                        .into_iter()
521                        .map(|label| label.map(Into::into))
522                        .collect()
523                })),
524            Extension::V0_1_0(ext) => Ok(ext
525                .call_labels_for_symbols(
526                    store,
527                    &language_server_id.0,
528                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
529                )
530                .await?
531                .map(|labels| {
532                    labels
533                        .into_iter()
534                        .map(|label| label.map(Into::into))
535                        .collect()
536                })),
537            Extension::V0_0_6(ext) => Ok(ext
538                .call_labels_for_symbols(
539                    store,
540                    &language_server_id.0,
541                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
542                )
543                .await?
544                .map(|labels| {
545                    labels
546                        .into_iter()
547                        .map(|label| label.map(Into::into))
548                        .collect()
549                })),
550            Extension::V0_0_1(_) | Extension::V0_0_4(_) => Ok(Ok(Vec::new())),
551        }
552    }
553
554    pub async fn call_complete_slash_command_argument(
555        &self,
556        store: &mut Store<WasmState>,
557        command: &SlashCommand,
558        arguments: &[String],
559    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
560        match self {
561            Extension::V0_4_0(ext) => {
562                ext.call_complete_slash_command_argument(store, command, arguments)
563                    .await
564            }
565            Extension::V0_3_0(ext) => {
566                ext.call_complete_slash_command_argument(store, command, arguments)
567                    .await
568            }
569            Extension::V0_2_0(ext) => {
570                ext.call_complete_slash_command_argument(store, command, arguments)
571                    .await
572            }
573            Extension::V0_1_0(ext) => {
574                ext.call_complete_slash_command_argument(store, command, arguments)
575                    .await
576            }
577            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
578                Ok(Ok(Vec::new()))
579            }
580        }
581    }
582
583    pub async fn call_run_slash_command(
584        &self,
585        store: &mut Store<WasmState>,
586        command: &SlashCommand,
587        arguments: &[String],
588        resource: Option<Resource<Arc<dyn WorktreeDelegate>>>,
589    ) -> Result<Result<SlashCommandOutput, String>> {
590        match self {
591            Extension::V0_4_0(ext) => {
592                ext.call_run_slash_command(store, command, arguments, resource)
593                    .await
594            }
595            Extension::V0_3_0(ext) => {
596                ext.call_run_slash_command(store, command, arguments, resource)
597                    .await
598            }
599            Extension::V0_2_0(ext) => {
600                ext.call_run_slash_command(store, command, arguments, resource)
601                    .await
602            }
603            Extension::V0_1_0(ext) => {
604                ext.call_run_slash_command(store, command, arguments, resource)
605                    .await
606            }
607            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
608                Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
609            }
610        }
611    }
612
613    pub async fn call_context_server_command(
614        &self,
615        store: &mut Store<WasmState>,
616        context_server_id: Arc<str>,
617        project: Resource<ExtensionProject>,
618    ) -> Result<Result<Command, String>> {
619        match self {
620            Extension::V0_4_0(ext) => {
621                ext.call_context_server_command(store, &context_server_id, project)
622                    .await
623            }
624            Extension::V0_3_0(ext) => {
625                ext.call_context_server_command(store, &context_server_id, project)
626                    .await
627            }
628            Extension::V0_2_0(ext) => Ok(ext
629                .call_context_server_command(store, &context_server_id, project)
630                .await?
631                .map(Into::into)),
632            Extension::V0_0_1(_)
633            | Extension::V0_0_4(_)
634            | Extension::V0_0_6(_)
635            | Extension::V0_1_0(_) => Err(anyhow!(
636                "`context_server_command` not available prior to v0.2.0"
637            )),
638        }
639    }
640
641    pub async fn call_suggest_docs_packages(
642        &self,
643        store: &mut Store<WasmState>,
644        provider: &str,
645    ) -> Result<Result<Vec<String>, String>> {
646        match self {
647            Extension::V0_4_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
648            Extension::V0_3_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
649            Extension::V0_2_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
650            Extension::V0_1_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
651            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => Err(anyhow!(
652                "`suggest_docs_packages` not available prior to v0.1.0"
653            )),
654        }
655    }
656
657    pub async fn call_index_docs(
658        &self,
659        store: &mut Store<WasmState>,
660        provider: &str,
661        package_name: &str,
662        kv_store: Resource<Arc<dyn KeyValueStoreDelegate>>,
663    ) -> Result<Result<(), String>> {
664        match self {
665            Extension::V0_4_0(ext) => {
666                ext.call_index_docs(store, provider, package_name, kv_store)
667                    .await
668            }
669            Extension::V0_3_0(ext) => {
670                ext.call_index_docs(store, provider, package_name, kv_store)
671                    .await
672            }
673            Extension::V0_2_0(ext) => {
674                ext.call_index_docs(store, provider, package_name, kv_store)
675                    .await
676            }
677            Extension::V0_1_0(ext) => {
678                ext.call_index_docs(store, provider, package_name, kv_store)
679                    .await
680            }
681            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
682                Err(anyhow!("`index_docs` not available prior to v0.1.0"))
683            }
684        }
685    }
686}
687
688trait ToWasmtimeResult<T> {
689    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
690}
691
692impl<T> ToWasmtimeResult<T> for Result<T> {
693    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
694        Ok(self.map_err(|error| error.to_string()))
695    }
696}