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;
  6// use indexed_docs::IndexedDocsDatabase;
  7use release_channel::ReleaseChannel;
  8use since_v0_2_0 as latest;
  9
 10use crate::DocsDatabase;
 11
 12use super::{wasm_engine, WasmState};
 13use anyhow::{anyhow, Context, Result};
 14use language::{LanguageServerName, LspAdapterDelegate};
 15use semantic_version::SemanticVersion;
 16use std::{ops::RangeInclusive, sync::Arc};
 17use wasmtime::{
 18    component::{Component, Linker, Resource},
 19    Store,
 20};
 21
 22#[cfg(test)]
 23pub use latest::CodeLabelSpanLiteral;
 24pub use latest::{
 25    zed::extension::lsp::{
 26        Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
 27    },
 28    zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
 29    CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
 30};
 31pub use since_v0_0_4::LanguageServerConfig;
 32
 33pub fn new_linker(
 34    f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
 35) -> Linker<WasmState> {
 36    let mut linker = Linker::new(&wasm_engine());
 37    wasmtime_wasi::add_to_linker_async(&mut linker).unwrap();
 38    f(&mut linker, wasi_view).unwrap();
 39    linker
 40}
 41
 42fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 43    state
 44}
 45
 46/// Returns whether the given Wasm API version is supported by the Wasm host.
 47pub fn is_supported_wasm_api_version(
 48    release_channel: ReleaseChannel,
 49    version: SemanticVersion,
 50) -> bool {
 51    wasm_api_version_range(release_channel).contains(&version)
 52}
 53
 54/// Returns the Wasm API version range that is supported by the Wasm host.
 55#[inline(always)]
 56pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
 57    // Note: The release channel can be used to stage a new version of the extension API.
 58    let _ = release_channel;
 59
 60    let max_version = match release_channel {
 61        ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
 62        ReleaseChannel::Stable | ReleaseChannel::Preview => since_v0_1_0::MAX_VERSION,
 63    };
 64
 65    since_v0_0_1::MIN_VERSION..=max_version
 66}
 67
 68pub enum Extension {
 69    V020(since_v0_2_0::Extension),
 70    V010(since_v0_1_0::Extension),
 71    V006(since_v0_0_6::Extension),
 72    V004(since_v0_0_4::Extension),
 73    V001(since_v0_0_1::Extension),
 74}
 75
 76impl Extension {
 77    pub async fn instantiate_async(
 78        store: &mut Store<WasmState>,
 79        release_channel: ReleaseChannel,
 80        version: SemanticVersion,
 81        component: &Component,
 82    ) -> Result<Self> {
 83        // Note: The release channel can be used to stage a new version of the extension API.
 84        let allow_latest_version = match release_channel {
 85            ReleaseChannel::Dev | ReleaseChannel::Nightly => true,
 86            ReleaseChannel::Stable | ReleaseChannel::Preview => false,
 87        };
 88
 89        if allow_latest_version && version >= latest::MIN_VERSION {
 90            let extension =
 91                latest::Extension::instantiate_async(store, component, latest::linker())
 92                    .await
 93                    .context("failed to instantiate wasm extension")?;
 94            Ok(Self::V020(extension))
 95        } else if version >= since_v0_1_0::MIN_VERSION {
 96            let extension = since_v0_1_0::Extension::instantiate_async(
 97                store,
 98                component,
 99                since_v0_1_0::linker(),
100            )
101            .await
102            .context("failed to instantiate wasm extension")?;
103            Ok(Self::V010(extension))
104        } else if version >= since_v0_0_6::MIN_VERSION {
105            let extension = since_v0_0_6::Extension::instantiate_async(
106                store,
107                component,
108                since_v0_0_6::linker(),
109            )
110            .await
111            .context("failed to instantiate wasm extension")?;
112            Ok(Self::V006(extension))
113        } else if version >= since_v0_0_4::MIN_VERSION {
114            let extension = since_v0_0_4::Extension::instantiate_async(
115                store,
116                component,
117                since_v0_0_4::linker(),
118            )
119            .await
120            .context("failed to instantiate wasm extension")?;
121            Ok(Self::V004(extension))
122        } else {
123            let extension = since_v0_0_1::Extension::instantiate_async(
124                store,
125                component,
126                since_v0_0_1::linker(),
127            )
128            .await
129            .context("failed to instantiate wasm extension")?;
130            Ok(Self::V001(extension))
131        }
132    }
133
134    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
135        match self {
136            Extension::V020(ext) => ext.call_init_extension(store).await,
137            Extension::V010(ext) => ext.call_init_extension(store).await,
138            Extension::V006(ext) => ext.call_init_extension(store).await,
139            Extension::V004(ext) => ext.call_init_extension(store).await,
140            Extension::V001(ext) => ext.call_init_extension(store).await,
141        }
142    }
143
144    pub async fn call_language_server_command(
145        &self,
146        store: &mut Store<WasmState>,
147        language_server_id: &LanguageServerName,
148        config: &LanguageServerConfig,
149        resource: Resource<Arc<dyn LspAdapterDelegate>>,
150    ) -> Result<Result<Command, String>> {
151        match self {
152            Extension::V020(ext) => {
153                ext.call_language_server_command(store, &language_server_id.0, resource)
154                    .await
155            }
156            Extension::V010(ext) => Ok(ext
157                .call_language_server_command(store, &language_server_id.0, resource)
158                .await?
159                .map(|command| command.into())),
160            Extension::V006(ext) => Ok(ext
161                .call_language_server_command(store, &language_server_id.0, resource)
162                .await?
163                .map(|command| command.into())),
164            Extension::V004(ext) => Ok(ext
165                .call_language_server_command(store, config, resource)
166                .await?
167                .map(|command| command.into())),
168            Extension::V001(ext) => Ok(ext
169                .call_language_server_command(store, &config.clone().into(), resource)
170                .await?
171                .map(|command| command.into())),
172        }
173    }
174
175    pub async fn call_language_server_initialization_options(
176        &self,
177        store: &mut Store<WasmState>,
178        language_server_id: &LanguageServerName,
179        config: &LanguageServerConfig,
180        resource: Resource<Arc<dyn LspAdapterDelegate>>,
181    ) -> Result<Result<Option<String>, String>> {
182        match self {
183            Extension::V020(ext) => {
184                ext.call_language_server_initialization_options(
185                    store,
186                    &language_server_id.0,
187                    resource,
188                )
189                .await
190            }
191            Extension::V010(ext) => {
192                ext.call_language_server_initialization_options(
193                    store,
194                    &language_server_id.0,
195                    resource,
196                )
197                .await
198            }
199            Extension::V006(ext) => {
200                ext.call_language_server_initialization_options(
201                    store,
202                    &language_server_id.0,
203                    resource,
204                )
205                .await
206            }
207            Extension::V004(ext) => {
208                ext.call_language_server_initialization_options(store, config, resource)
209                    .await
210            }
211            Extension::V001(ext) => {
212                ext.call_language_server_initialization_options(
213                    store,
214                    &config.clone().into(),
215                    resource,
216                )
217                .await
218            }
219        }
220    }
221
222    pub async fn call_language_server_workspace_configuration(
223        &self,
224        store: &mut Store<WasmState>,
225        language_server_id: &LanguageServerName,
226        resource: Resource<Arc<dyn LspAdapterDelegate>>,
227    ) -> Result<Result<Option<String>, String>> {
228        match self {
229            Extension::V020(ext) => {
230                ext.call_language_server_workspace_configuration(
231                    store,
232                    &language_server_id.0,
233                    resource,
234                )
235                .await
236            }
237            Extension::V010(ext) => {
238                ext.call_language_server_workspace_configuration(
239                    store,
240                    &language_server_id.0,
241                    resource,
242                )
243                .await
244            }
245            Extension::V006(ext) => {
246                ext.call_language_server_workspace_configuration(
247                    store,
248                    &language_server_id.0,
249                    resource,
250                )
251                .await
252            }
253            Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
254        }
255    }
256
257    pub async fn call_labels_for_completions(
258        &self,
259        store: &mut Store<WasmState>,
260        language_server_id: &LanguageServerName,
261        completions: Vec<latest::Completion>,
262    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
263        match self {
264            Extension::V020(ext) => {
265                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
266                    .await
267            }
268            Extension::V010(ext) => Ok(ext
269                .call_labels_for_completions(
270                    store,
271                    &language_server_id.0,
272                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
273                )
274                .await?
275                .map(|labels| {
276                    labels
277                        .into_iter()
278                        .map(|label| label.map(Into::into))
279                        .collect()
280                })),
281            Extension::V006(ext) => Ok(ext
282                .call_labels_for_completions(
283                    store,
284                    &language_server_id.0,
285                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
286                )
287                .await?
288                .map(|labels| {
289                    labels
290                        .into_iter()
291                        .map(|label| label.map(Into::into))
292                        .collect()
293                })),
294            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
295        }
296    }
297
298    pub async fn call_labels_for_symbols(
299        &self,
300        store: &mut Store<WasmState>,
301        language_server_id: &LanguageServerName,
302        symbols: Vec<latest::Symbol>,
303    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
304        match self {
305            Extension::V020(ext) => {
306                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
307                    .await
308            }
309            Extension::V010(ext) => Ok(ext
310                .call_labels_for_symbols(
311                    store,
312                    &language_server_id.0,
313                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
314                )
315                .await?
316                .map(|labels| {
317                    labels
318                        .into_iter()
319                        .map(|label| label.map(Into::into))
320                        .collect()
321                })),
322            Extension::V006(ext) => Ok(ext
323                .call_labels_for_symbols(
324                    store,
325                    &language_server_id.0,
326                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
327                )
328                .await?
329                .map(|labels| {
330                    labels
331                        .into_iter()
332                        .map(|label| label.map(Into::into))
333                        .collect()
334                })),
335            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
336        }
337    }
338
339    pub async fn call_complete_slash_command_argument(
340        &self,
341        store: &mut Store<WasmState>,
342        command: &SlashCommand,
343        arguments: &[String],
344    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
345        match self {
346            Extension::V020(ext) => {
347                ext.call_complete_slash_command_argument(store, command, arguments)
348                    .await
349            }
350            Extension::V010(ext) => {
351                ext.call_complete_slash_command_argument(store, command, arguments)
352                    .await
353            }
354            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
355        }
356    }
357
358    pub async fn call_run_slash_command(
359        &self,
360        store: &mut Store<WasmState>,
361        command: &SlashCommand,
362        arguments: &[String],
363        resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
364    ) -> Result<Result<SlashCommandOutput, String>> {
365        match self {
366            Extension::V020(ext) => {
367                ext.call_run_slash_command(store, command, arguments, resource)
368                    .await
369            }
370            Extension::V010(ext) => {
371                ext.call_run_slash_command(store, command, arguments, resource)
372                    .await
373            }
374            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
375                Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
376            }
377        }
378    }
379
380    pub async fn call_suggest_docs_packages(
381        &self,
382        store: &mut Store<WasmState>,
383        provider: &str,
384    ) -> Result<Result<Vec<String>, String>> {
385        match self {
386            Extension::V020(ext) => ext.call_suggest_docs_packages(store, provider).await,
387            Extension::V010(ext) => ext.call_suggest_docs_packages(store, provider).await,
388            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Err(anyhow!(
389                "`suggest_docs_packages` not available prior to v0.1.0"
390            )),
391        }
392    }
393
394    pub async fn call_index_docs(
395        &self,
396        store: &mut Store<WasmState>,
397        provider: &str,
398        package_name: &str,
399        database: Resource<Arc<dyn DocsDatabase>>,
400    ) -> Result<Result<(), String>> {
401        match self {
402            Extension::V020(ext) => {
403                ext.call_index_docs(store, provider, package_name, database)
404                    .await
405            }
406            Extension::V010(ext) => {
407                ext.call_index_docs(store, provider, package_name, database)
408                    .await
409            }
410            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
411                Err(anyhow!("`index_docs` not available prior to v0.1.0"))
412            }
413        }
414    }
415}
416
417trait ToWasmtimeResult<T> {
418    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
419}
420
421impl<T> ToWasmtimeResult<T> for Result<T> {
422    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
423        Ok(self.map_err(|error| error.to_string()))
424    }
425}