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::{wasm_engine, WasmState};
 15use anyhow::{anyhow, Context as _, Result};
 16use semantic_version::SemanticVersion;
 17use std::{ops::RangeInclusive, sync::Arc};
 18use wasmtime::{
 19    component::{Component, Linker, Resource},
 20    Store,
 21};
 22
 23#[cfg(test)]
 24pub use latest::CodeLabelSpanLiteral;
 25pub use latest::{
 26    zed::extension::lsp::{
 27        Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
 28    },
 29    zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
 30    CodeLabel, CodeLabelSpan, Command, ExtensionProject, Range, SlashCommand,
 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    V040(since_v0_4_0::Extension),
 94    V030(since_v0_3_0::Extension),
 95    V020(since_v0_2_0::Extension),
 96    V010(since_v0_1_0::Extension),
 97    V006(since_v0_0_6::Extension),
 98    V004(since_v0_0_4::Extension),
 99    V001(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::V040(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::V030(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::V020(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::V010(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::V006(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::V004(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::V001(extension))
174        }
175    }
176
177    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
178        match self {
179            Extension::V040(ext) => ext.call_init_extension(store).await,
180            Extension::V030(ext) => ext.call_init_extension(store).await,
181            Extension::V020(ext) => ext.call_init_extension(store).await,
182            Extension::V010(ext) => ext.call_init_extension(store).await,
183            Extension::V006(ext) => ext.call_init_extension(store).await,
184            Extension::V004(ext) => ext.call_init_extension(store).await,
185            Extension::V001(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::V040(ext) => {
198                ext.call_language_server_command(store, &language_server_id.0, resource)
199                    .await
200            }
201            Extension::V030(ext) => {
202                ext.call_language_server_command(store, &language_server_id.0, resource)
203                    .await
204            }
205            Extension::V020(ext) => Ok(ext
206                .call_language_server_command(store, &language_server_id.0, resource)
207                .await?
208                .map(|command| command.into())),
209            Extension::V010(ext) => Ok(ext
210                .call_language_server_command(store, &language_server_id.0, resource)
211                .await?
212                .map(|command| command.into())),
213            Extension::V006(ext) => Ok(ext
214                .call_language_server_command(store, &language_server_id.0, resource)
215                .await?
216                .map(|command| command.into())),
217            Extension::V004(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::V001(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::V040(ext) => {
252                ext.call_language_server_initialization_options(
253                    store,
254                    &language_server_id.0,
255                    resource,
256                )
257                .await
258            }
259            Extension::V030(ext) => {
260                ext.call_language_server_initialization_options(
261                    store,
262                    &language_server_id.0,
263                    resource,
264                )
265                .await
266            }
267            Extension::V020(ext) => {
268                ext.call_language_server_initialization_options(
269                    store,
270                    &language_server_id.0,
271                    resource,
272                )
273                .await
274            }
275            Extension::V010(ext) => {
276                ext.call_language_server_initialization_options(
277                    store,
278                    &language_server_id.0,
279                    resource,
280                )
281                .await
282            }
283            Extension::V006(ext) => {
284                ext.call_language_server_initialization_options(
285                    store,
286                    &language_server_id.0,
287                    resource,
288                )
289                .await
290            }
291            Extension::V004(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::V001(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::V040(ext) => {
325                ext.call_language_server_workspace_configuration(
326                    store,
327                    &language_server_id.0,
328                    resource,
329                )
330                .await
331            }
332            Extension::V030(ext) => {
333                ext.call_language_server_workspace_configuration(
334                    store,
335                    &language_server_id.0,
336                    resource,
337                )
338                .await
339            }
340            Extension::V020(ext) => {
341                ext.call_language_server_workspace_configuration(
342                    store,
343                    &language_server_id.0,
344                    resource,
345                )
346                .await
347            }
348            Extension::V010(ext) => {
349                ext.call_language_server_workspace_configuration(
350                    store,
351                    &language_server_id.0,
352                    resource,
353                )
354                .await
355            }
356            Extension::V006(ext) => {
357                ext.call_language_server_workspace_configuration(
358                    store,
359                    &language_server_id.0,
360                    resource,
361                )
362                .await
363            }
364            Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
365        }
366    }
367
368    pub async fn call_labels_for_completions(
369        &self,
370        store: &mut Store<WasmState>,
371        language_server_id: &LanguageServerName,
372        completions: Vec<latest::Completion>,
373    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
374        match self {
375            Extension::V040(ext) => {
376                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
377                    .await
378            }
379            Extension::V030(ext) => Ok(ext
380                .call_labels_for_completions(
381                    store,
382                    &language_server_id.0,
383                    &completions.into_iter().collect::<Vec<_>>(),
384                )
385                .await?
386                .map(|labels| {
387                    labels
388                        .into_iter()
389                        .map(|label| label.map(Into::into))
390                        .collect()
391                })),
392            Extension::V020(ext) => Ok(ext
393                .call_labels_for_completions(
394                    store,
395                    &language_server_id.0,
396                    &completions.into_iter().collect::<Vec<_>>(),
397                )
398                .await?
399                .map(|labels| {
400                    labels
401                        .into_iter()
402                        .map(|label| label.map(Into::into))
403                        .collect()
404                })),
405            Extension::V010(ext) => Ok(ext
406                .call_labels_for_completions(
407                    store,
408                    &language_server_id.0,
409                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
410                )
411                .await?
412                .map(|labels| {
413                    labels
414                        .into_iter()
415                        .map(|label| label.map(Into::into))
416                        .collect()
417                })),
418            Extension::V006(ext) => Ok(ext
419                .call_labels_for_completions(
420                    store,
421                    &language_server_id.0,
422                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
423                )
424                .await?
425                .map(|labels| {
426                    labels
427                        .into_iter()
428                        .map(|label| label.map(Into::into))
429                        .collect()
430                })),
431            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
432        }
433    }
434
435    pub async fn call_labels_for_symbols(
436        &self,
437        store: &mut Store<WasmState>,
438        language_server_id: &LanguageServerName,
439        symbols: Vec<latest::Symbol>,
440    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
441        match self {
442            Extension::V040(ext) => {
443                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
444                    .await
445            }
446            Extension::V030(ext) => Ok(ext
447                .call_labels_for_symbols(
448                    store,
449                    &language_server_id.0,
450                    &symbols.into_iter().collect::<Vec<_>>(),
451                )
452                .await?
453                .map(|labels| {
454                    labels
455                        .into_iter()
456                        .map(|label| label.map(Into::into))
457                        .collect()
458                })),
459            Extension::V020(ext) => Ok(ext
460                .call_labels_for_symbols(
461                    store,
462                    &language_server_id.0,
463                    &symbols.into_iter().collect::<Vec<_>>(),
464                )
465                .await?
466                .map(|labels| {
467                    labels
468                        .into_iter()
469                        .map(|label| label.map(Into::into))
470                        .collect()
471                })),
472            Extension::V010(ext) => Ok(ext
473                .call_labels_for_symbols(
474                    store,
475                    &language_server_id.0,
476                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
477                )
478                .await?
479                .map(|labels| {
480                    labels
481                        .into_iter()
482                        .map(|label| label.map(Into::into))
483                        .collect()
484                })),
485            Extension::V006(ext) => Ok(ext
486                .call_labels_for_symbols(
487                    store,
488                    &language_server_id.0,
489                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
490                )
491                .await?
492                .map(|labels| {
493                    labels
494                        .into_iter()
495                        .map(|label| label.map(Into::into))
496                        .collect()
497                })),
498            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
499        }
500    }
501
502    pub async fn call_complete_slash_command_argument(
503        &self,
504        store: &mut Store<WasmState>,
505        command: &SlashCommand,
506        arguments: &[String],
507    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
508        match self {
509            Extension::V040(ext) => {
510                ext.call_complete_slash_command_argument(store, command, arguments)
511                    .await
512            }
513            Extension::V030(ext) => {
514                ext.call_complete_slash_command_argument(store, command, arguments)
515                    .await
516            }
517            Extension::V020(ext) => {
518                ext.call_complete_slash_command_argument(store, command, arguments)
519                    .await
520            }
521            Extension::V010(ext) => {
522                ext.call_complete_slash_command_argument(store, command, arguments)
523                    .await
524            }
525            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
526        }
527    }
528
529    pub async fn call_run_slash_command(
530        &self,
531        store: &mut Store<WasmState>,
532        command: &SlashCommand,
533        arguments: &[String],
534        resource: Option<Resource<Arc<dyn WorktreeDelegate>>>,
535    ) -> Result<Result<SlashCommandOutput, String>> {
536        match self {
537            Extension::V040(ext) => {
538                ext.call_run_slash_command(store, command, arguments, resource)
539                    .await
540            }
541            Extension::V030(ext) => {
542                ext.call_run_slash_command(store, command, arguments, resource)
543                    .await
544            }
545            Extension::V020(ext) => {
546                ext.call_run_slash_command(store, command, arguments, resource)
547                    .await
548            }
549            Extension::V010(ext) => {
550                ext.call_run_slash_command(store, command, arguments, resource)
551                    .await
552            }
553            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
554                Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
555            }
556        }
557    }
558
559    pub async fn call_context_server_command(
560        &self,
561        store: &mut Store<WasmState>,
562        context_server_id: Arc<str>,
563        project: Resource<ExtensionProject>,
564    ) -> Result<Result<Command, String>> {
565        match self {
566            Extension::V040(ext) => {
567                ext.call_context_server_command(store, &context_server_id, project)
568                    .await
569            }
570            Extension::V030(ext) => {
571                ext.call_context_server_command(store, &context_server_id, project)
572                    .await
573            }
574            Extension::V020(ext) => Ok(ext
575                .call_context_server_command(store, &context_server_id, project)
576                .await?
577                .map(Into::into)),
578            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) | Extension::V010(_) => {
579                Err(anyhow!(
580                    "`context_server_command` not available prior to v0.2.0"
581                ))
582            }
583        }
584    }
585
586    pub async fn call_suggest_docs_packages(
587        &self,
588        store: &mut Store<WasmState>,
589        provider: &str,
590    ) -> Result<Result<Vec<String>, String>> {
591        match self {
592            Extension::V040(ext) => ext.call_suggest_docs_packages(store, provider).await,
593            Extension::V030(ext) => ext.call_suggest_docs_packages(store, provider).await,
594            Extension::V020(ext) => ext.call_suggest_docs_packages(store, provider).await,
595            Extension::V010(ext) => ext.call_suggest_docs_packages(store, provider).await,
596            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Err(anyhow!(
597                "`suggest_docs_packages` not available prior to v0.1.0"
598            )),
599        }
600    }
601
602    pub async fn call_index_docs(
603        &self,
604        store: &mut Store<WasmState>,
605        provider: &str,
606        package_name: &str,
607        kv_store: Resource<Arc<dyn KeyValueStoreDelegate>>,
608    ) -> Result<Result<(), String>> {
609        match self {
610            Extension::V040(ext) => {
611                ext.call_index_docs(store, provider, package_name, kv_store)
612                    .await
613            }
614            Extension::V030(ext) => {
615                ext.call_index_docs(store, provider, package_name, kv_store)
616                    .await
617            }
618            Extension::V020(ext) => {
619                ext.call_index_docs(store, provider, package_name, kv_store)
620                    .await
621            }
622            Extension::V010(ext) => {
623                ext.call_index_docs(store, provider, package_name, kv_store)
624                    .await
625            }
626            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
627                Err(anyhow!("`index_docs` not available prior to v0.1.0"))
628            }
629        }
630    }
631}
632
633trait ToWasmtimeResult<T> {
634    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
635}
636
637impl<T> ToWasmtimeResult<T> for Result<T> {
638    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
639        Ok(self.map_err(|error| error.to_string()))
640    }
641}