wit.rs

  1mod since_v0_0_1;
  2mod since_v0_0_4;
  3mod since_v0_0_6;
  4mod since_v0_0_7;
  5use indexed_docs::IndexedDocsDatabase;
  6use release_channel::ReleaseChannel;
  7use since_v0_0_7 as latest;
  8
  9use super::{wasm_engine, WasmState};
 10use anyhow::{anyhow, Context, Result};
 11use language::{LanguageServerName, LspAdapterDelegate};
 12use semantic_version::SemanticVersion;
 13use std::{ops::RangeInclusive, sync::Arc};
 14use wasmtime::{
 15    component::{Component, Instance, Linker, Resource},
 16    Store,
 17};
 18
 19#[cfg(test)]
 20pub use latest::CodeLabelSpanLiteral;
 21pub use latest::{
 22    zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
 23    zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
 24    CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
 25};
 26pub use since_v0_0_4::LanguageServerConfig;
 27
 28pub fn new_linker(
 29    f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
 30) -> Linker<WasmState> {
 31    let mut linker = Linker::new(&wasm_engine());
 32    wasmtime_wasi::add_to_linker_async(&mut linker).unwrap();
 33    f(&mut linker, wasi_view).unwrap();
 34    linker
 35}
 36
 37fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 38    state
 39}
 40
 41/// Returns whether the given Wasm API version is supported by the Wasm host.
 42pub fn is_supported_wasm_api_version(
 43    release_channel: ReleaseChannel,
 44    version: SemanticVersion,
 45) -> bool {
 46    wasm_api_version_range(release_channel).contains(&version)
 47}
 48
 49/// Returns the Wasm API version range that is supported by the Wasm host.
 50#[inline(always)]
 51pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
 52    let max_version = match release_channel {
 53        ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
 54        ReleaseChannel::Stable | ReleaseChannel::Preview => since_v0_0_6::MAX_VERSION,
 55    };
 56
 57    since_v0_0_1::MIN_VERSION..=max_version
 58}
 59
 60pub enum Extension {
 61    V007(since_v0_0_7::Extension),
 62    V006(since_v0_0_6::Extension),
 63    V004(since_v0_0_4::Extension),
 64    V001(since_v0_0_1::Extension),
 65}
 66
 67impl Extension {
 68    pub async fn instantiate_async(
 69        store: &mut Store<WasmState>,
 70        release_channel: ReleaseChannel,
 71        version: SemanticVersion,
 72        component: &Component,
 73    ) -> Result<(Self, Instance)> {
 74        if release_channel == ReleaseChannel::Dev && version >= latest::MIN_VERSION {
 75            let (extension, instance) =
 76                latest::Extension::instantiate_async(store, &component, latest::linker())
 77                    .await
 78                    .context("failed to instantiate wasm extension")?;
 79            Ok((Self::V007(extension), instance))
 80        } else if version >= since_v0_0_6::MIN_VERSION {
 81            let (extension, instance) = since_v0_0_6::Extension::instantiate_async(
 82                store,
 83                &component,
 84                since_v0_0_6::linker(),
 85            )
 86            .await
 87            .context("failed to instantiate wasm extension")?;
 88            Ok((Self::V006(extension), instance))
 89        } else if version >= since_v0_0_4::MIN_VERSION {
 90            let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
 91                store,
 92                &component,
 93                since_v0_0_4::linker(),
 94            )
 95            .await
 96            .context("failed to instantiate wasm extension")?;
 97            Ok((Self::V004(extension), instance))
 98        } else {
 99            let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
100                store,
101                &component,
102                since_v0_0_1::linker(),
103            )
104            .await
105            .context("failed to instantiate wasm extension")?;
106            Ok((Self::V001(extension), instance))
107        }
108    }
109
110    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
111        match self {
112            Extension::V007(ext) => ext.call_init_extension(store).await,
113            Extension::V006(ext) => ext.call_init_extension(store).await,
114            Extension::V004(ext) => ext.call_init_extension(store).await,
115            Extension::V001(ext) => ext.call_init_extension(store).await,
116        }
117    }
118
119    pub async fn call_language_server_command(
120        &self,
121        store: &mut Store<WasmState>,
122        language_server_id: &LanguageServerName,
123        config: &LanguageServerConfig,
124        resource: Resource<Arc<dyn LspAdapterDelegate>>,
125    ) -> Result<Result<Command, String>> {
126        match self {
127            Extension::V007(ext) => {
128                ext.call_language_server_command(store, &language_server_id.0, resource)
129                    .await
130            }
131            Extension::V006(ext) => Ok(ext
132                .call_language_server_command(store, &language_server_id.0, resource)
133                .await?
134                .map(|command| command.into())),
135            Extension::V004(ext) => Ok(ext
136                .call_language_server_command(store, config, resource)
137                .await?
138                .map(|command| command.into())),
139            Extension::V001(ext) => Ok(ext
140                .call_language_server_command(store, &config.clone().into(), resource)
141                .await?
142                .map(|command| command.into())),
143        }
144    }
145
146    pub async fn call_language_server_initialization_options(
147        &self,
148        store: &mut Store<WasmState>,
149        language_server_id: &LanguageServerName,
150        config: &LanguageServerConfig,
151        resource: Resource<Arc<dyn LspAdapterDelegate>>,
152    ) -> Result<Result<Option<String>, String>> {
153        match self {
154            Extension::V007(ext) => {
155                ext.call_language_server_initialization_options(
156                    store,
157                    &language_server_id.0,
158                    resource,
159                )
160                .await
161            }
162            Extension::V006(ext) => {
163                ext.call_language_server_initialization_options(
164                    store,
165                    &language_server_id.0,
166                    resource,
167                )
168                .await
169            }
170            Extension::V004(ext) => {
171                ext.call_language_server_initialization_options(store, config, resource)
172                    .await
173            }
174            Extension::V001(ext) => {
175                ext.call_language_server_initialization_options(
176                    store,
177                    &config.clone().into(),
178                    resource,
179                )
180                .await
181            }
182        }
183    }
184
185    pub async fn call_language_server_workspace_configuration(
186        &self,
187        store: &mut Store<WasmState>,
188        language_server_id: &LanguageServerName,
189        resource: Resource<Arc<dyn LspAdapterDelegate>>,
190    ) -> Result<Result<Option<String>, String>> {
191        match self {
192            Extension::V007(ext) => {
193                ext.call_language_server_workspace_configuration(
194                    store,
195                    &language_server_id.0,
196                    resource,
197                )
198                .await
199            }
200            Extension::V006(ext) => {
201                ext.call_language_server_workspace_configuration(
202                    store,
203                    &language_server_id.0,
204                    resource,
205                )
206                .await
207            }
208            Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
209        }
210    }
211
212    pub async fn call_labels_for_completions(
213        &self,
214        store: &mut Store<WasmState>,
215        language_server_id: &LanguageServerName,
216        completions: Vec<latest::Completion>,
217    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
218        match self {
219            Extension::V007(ext) => {
220                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
221                    .await
222            }
223            Extension::V006(ext) => Ok(ext
224                .call_labels_for_completions(store, &language_server_id.0, &completions)
225                .await?
226                .map(|labels| {
227                    labels
228                        .into_iter()
229                        .map(|label| label.map(Into::into))
230                        .collect()
231                })),
232            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
233        }
234    }
235
236    pub async fn call_labels_for_symbols(
237        &self,
238        store: &mut Store<WasmState>,
239        language_server_id: &LanguageServerName,
240        symbols: Vec<latest::Symbol>,
241    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
242        match self {
243            Extension::V007(ext) => {
244                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
245                    .await
246            }
247            Extension::V006(ext) => Ok(ext
248                .call_labels_for_symbols(store, &language_server_id.0, &symbols)
249                .await?
250                .map(|labels| {
251                    labels
252                        .into_iter()
253                        .map(|label| label.map(Into::into))
254                        .collect()
255                })),
256            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
257        }
258    }
259
260    pub async fn call_complete_slash_command_argument(
261        &self,
262        store: &mut Store<WasmState>,
263        command: &SlashCommand,
264        query: &str,
265    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
266        match self {
267            Extension::V007(ext) => {
268                ext.call_complete_slash_command_argument(store, command, query)
269                    .await
270            }
271            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
272        }
273    }
274
275    pub async fn call_run_slash_command(
276        &self,
277        store: &mut Store<WasmState>,
278        command: &SlashCommand,
279        argument: Option<&str>,
280        resource: Resource<Arc<dyn LspAdapterDelegate>>,
281    ) -> Result<Result<SlashCommandOutput, String>> {
282        match self {
283            Extension::V007(ext) => {
284                ext.call_run_slash_command(store, command, argument, resource)
285                    .await
286            }
287            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
288                Err(anyhow!("`run_slash_command` not available prior to v0.0.7"))
289            }
290        }
291    }
292
293    pub async fn call_index_docs(
294        &self,
295        store: &mut Store<WasmState>,
296        provider: &str,
297        package_name: &str,
298        database: Resource<Arc<IndexedDocsDatabase>>,
299    ) -> Result<Result<(), String>> {
300        match self {
301            Extension::V007(ext) => {
302                ext.call_index_docs(store, provider, package_name, database)
303                    .await
304            }
305            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
306                Err(anyhow!("`index_docs` not available prior to v0.0.7"))
307            }
308        }
309    }
310}
311
312trait ToWasmtimeResult<T> {
313    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
314}
315
316impl<T> ToWasmtimeResult<T> for Result<T> {
317    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
318        Ok(self.map_err(|error| error.to_string()))
319    }
320}