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::command::add_to_linker(&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 = if release_channel == ReleaseChannel::Dev {
 53        latest::MAX_VERSION
 54    } else {
 55        since_v0_0_6::MAX_VERSION
 56    };
 57
 58    since_v0_0_1::MIN_VERSION..=max_version
 59}
 60
 61pub enum Extension {
 62    V007(since_v0_0_7::Extension),
 63    V006(since_v0_0_6::Extension),
 64    V004(since_v0_0_4::Extension),
 65    V001(since_v0_0_1::Extension),
 66}
 67
 68impl Extension {
 69    pub async fn instantiate_async(
 70        store: &mut Store<WasmState>,
 71        release_channel: ReleaseChannel,
 72        version: SemanticVersion,
 73        component: &Component,
 74    ) -> Result<(Self, Instance)> {
 75        if release_channel == ReleaseChannel::Dev && version >= latest::MIN_VERSION {
 76            let (extension, instance) =
 77                latest::Extension::instantiate_async(store, &component, latest::linker())
 78                    .await
 79                    .context("failed to instantiate wasm extension")?;
 80            Ok((Self::V007(extension), instance))
 81        } else if version >= since_v0_0_6::MIN_VERSION {
 82            let (extension, instance) = since_v0_0_6::Extension::instantiate_async(
 83                store,
 84                &component,
 85                since_v0_0_6::linker(),
 86            )
 87            .await
 88            .context("failed to instantiate wasm extension")?;
 89            Ok((Self::V006(extension), instance))
 90        } else if version >= since_v0_0_4::MIN_VERSION {
 91            let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
 92                store,
 93                &component,
 94                since_v0_0_4::linker(),
 95            )
 96            .await
 97            .context("failed to instantiate wasm extension")?;
 98            Ok((Self::V004(extension), instance))
 99        } else {
100            let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
101                store,
102                &component,
103                since_v0_0_1::linker(),
104            )
105            .await
106            .context("failed to instantiate wasm extension")?;
107            Ok((Self::V001(extension), instance))
108        }
109    }
110
111    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
112        match self {
113            Extension::V007(ext) => ext.call_init_extension(store).await,
114            Extension::V006(ext) => ext.call_init_extension(store).await,
115            Extension::V004(ext) => ext.call_init_extension(store).await,
116            Extension::V001(ext) => ext.call_init_extension(store).await,
117        }
118    }
119
120    pub async fn call_language_server_command(
121        &self,
122        store: &mut Store<WasmState>,
123        language_server_id: &LanguageServerName,
124        config: &LanguageServerConfig,
125        resource: Resource<Arc<dyn LspAdapterDelegate>>,
126    ) -> Result<Result<Command, String>> {
127        match self {
128            Extension::V007(ext) => {
129                ext.call_language_server_command(store, &language_server_id.0, resource)
130                    .await
131            }
132            Extension::V006(ext) => Ok(ext
133                .call_language_server_command(store, &language_server_id.0, resource)
134                .await?
135                .map(|command| command.into())),
136            Extension::V004(ext) => Ok(ext
137                .call_language_server_command(store, config, resource)
138                .await?
139                .map(|command| command.into())),
140            Extension::V001(ext) => Ok(ext
141                .call_language_server_command(store, &config.clone().into(), resource)
142                .await?
143                .map(|command| command.into())),
144        }
145    }
146
147    pub async fn call_language_server_initialization_options(
148        &self,
149        store: &mut Store<WasmState>,
150        language_server_id: &LanguageServerName,
151        config: &LanguageServerConfig,
152        resource: Resource<Arc<dyn LspAdapterDelegate>>,
153    ) -> Result<Result<Option<String>, String>> {
154        match self {
155            Extension::V007(ext) => {
156                ext.call_language_server_initialization_options(
157                    store,
158                    &language_server_id.0,
159                    resource,
160                )
161                .await
162            }
163            Extension::V006(ext) => {
164                ext.call_language_server_initialization_options(
165                    store,
166                    &language_server_id.0,
167                    resource,
168                )
169                .await
170            }
171            Extension::V004(ext) => {
172                ext.call_language_server_initialization_options(store, config, resource)
173                    .await
174            }
175            Extension::V001(ext) => {
176                ext.call_language_server_initialization_options(
177                    store,
178                    &config.clone().into(),
179                    resource,
180                )
181                .await
182            }
183        }
184    }
185
186    pub async fn call_language_server_workspace_configuration(
187        &self,
188        store: &mut Store<WasmState>,
189        language_server_id: &LanguageServerName,
190        resource: Resource<Arc<dyn LspAdapterDelegate>>,
191    ) -> Result<Result<Option<String>, String>> {
192        match self {
193            Extension::V007(ext) => {
194                ext.call_language_server_workspace_configuration(
195                    store,
196                    &language_server_id.0,
197                    resource,
198                )
199                .await
200            }
201            Extension::V006(ext) => {
202                ext.call_language_server_workspace_configuration(
203                    store,
204                    &language_server_id.0,
205                    resource,
206                )
207                .await
208            }
209            Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
210        }
211    }
212
213    pub async fn call_labels_for_completions(
214        &self,
215        store: &mut Store<WasmState>,
216        language_server_id: &LanguageServerName,
217        completions: Vec<latest::Completion>,
218    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
219        match self {
220            Extension::V007(ext) => {
221                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
222                    .await
223            }
224            Extension::V006(ext) => Ok(ext
225                .call_labels_for_completions(store, &language_server_id.0, &completions)
226                .await?
227                .map(|labels| {
228                    labels
229                        .into_iter()
230                        .map(|label| label.map(Into::into))
231                        .collect()
232                })),
233            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
234        }
235    }
236
237    pub async fn call_labels_for_symbols(
238        &self,
239        store: &mut Store<WasmState>,
240        language_server_id: &LanguageServerName,
241        symbols: Vec<latest::Symbol>,
242    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
243        match self {
244            Extension::V007(ext) => {
245                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
246                    .await
247            }
248            Extension::V006(ext) => Ok(ext
249                .call_labels_for_symbols(store, &language_server_id.0, &symbols)
250                .await?
251                .map(|labels| {
252                    labels
253                        .into_iter()
254                        .map(|label| label.map(Into::into))
255                        .collect()
256                })),
257            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
258        }
259    }
260
261    pub async fn call_complete_slash_command_argument(
262        &self,
263        store: &mut Store<WasmState>,
264        command: &SlashCommand,
265        query: &str,
266    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
267        match self {
268            Extension::V007(ext) => {
269                ext.call_complete_slash_command_argument(store, command, query)
270                    .await
271            }
272            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
273        }
274    }
275
276    pub async fn call_run_slash_command(
277        &self,
278        store: &mut Store<WasmState>,
279        command: &SlashCommand,
280        argument: Option<&str>,
281        resource: Resource<Arc<dyn LspAdapterDelegate>>,
282    ) -> Result<Result<SlashCommandOutput, String>> {
283        match self {
284            Extension::V007(ext) => {
285                ext.call_run_slash_command(store, command, argument, resource)
286                    .await
287            }
288            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
289                Err(anyhow!("`run_slash_command` not available prior to v0.0.7"))
290            }
291        }
292    }
293
294    pub async fn call_index_docs(
295        &self,
296        store: &mut Store<WasmState>,
297        provider: &str,
298        package_name: &str,
299        database: Resource<Arc<IndexedDocsDatabase>>,
300    ) -> Result<Result<(), String>> {
301        match self {
302            Extension::V007(ext) => {
303                ext.call_index_docs(store, provider, package_name, database)
304                    .await
305            }
306            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
307                Err(anyhow!("`index_docs` not available prior to v0.0.7"))
308            }
309        }
310    }
311}
312
313trait ToWasmtimeResult<T> {
314    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
315}
316
317impl<T> ToWasmtimeResult<T> for Result<T> {
318    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
319        Ok(self.map_err(|error| error.to_string()))
320    }
321}