wit.rs

  1mod since_v0_0_1;
  2mod since_v0_0_4;
  3mod since_v0_0_6;
  4mod since_v0_0_7;
  5use release_channel::ReleaseChannel;
  6use since_v0_0_7 as latest;
  7
  8use super::{wasm_engine, WasmState};
  9use anyhow::{anyhow, Context, Result};
 10use language::{LanguageServerName, LspAdapterDelegate};
 11use semantic_version::SemanticVersion;
 12use std::{ops::RangeInclusive, sync::Arc};
 13use wasmtime::{
 14    component::{Component, Instance, Linker, Resource},
 15    Store,
 16};
 17
 18#[cfg(test)]
 19pub use latest::CodeLabelSpanLiteral;
 20pub use latest::{
 21    zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
 22    zed::extension::slash_command::SlashCommandOutput,
 23    CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
 24};
 25pub use since_v0_0_4::LanguageServerConfig;
 26
 27pub fn new_linker(
 28    f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
 29) -> Linker<WasmState> {
 30    let mut linker = Linker::new(&wasm_engine());
 31    wasmtime_wasi::command::add_to_linker(&mut linker).unwrap();
 32    f(&mut linker, wasi_view).unwrap();
 33    linker
 34}
 35
 36fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 37    state
 38}
 39
 40/// Returns whether the given Wasm API version is supported by the Wasm host.
 41pub fn is_supported_wasm_api_version(
 42    release_channel: ReleaseChannel,
 43    version: SemanticVersion,
 44) -> bool {
 45    wasm_api_version_range(release_channel).contains(&version)
 46}
 47
 48/// Returns the Wasm API version range that is supported by the Wasm host.
 49#[inline(always)]
 50pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
 51    let max_version = if release_channel == ReleaseChannel::Dev {
 52        latest::MAX_VERSION
 53    } else {
 54        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<String>, 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
294trait ToWasmtimeResult<T> {
295    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
296}
297
298impl<T> ToWasmtimeResult<T> for Result<T> {
299    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
300        Ok(self.map_err(|error| error.to_string()))
301    }
302}