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::{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    CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
 23};
 24pub use since_v0_0_4::LanguageServerConfig;
 25
 26pub fn new_linker(
 27    f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
 28) -> Linker<WasmState> {
 29    let mut linker = Linker::new(&wasm_engine());
 30    wasmtime_wasi::command::add_to_linker(&mut linker).unwrap();
 31    f(&mut linker, wasi_view).unwrap();
 32    linker
 33}
 34
 35fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 36    state
 37}
 38
 39/// Returns whether the given Wasm API version is supported by the Wasm host.
 40pub fn is_supported_wasm_api_version(
 41    release_channel: ReleaseChannel,
 42    version: SemanticVersion,
 43) -> bool {
 44    wasm_api_version_range(release_channel).contains(&version)
 45}
 46
 47/// Returns the Wasm API version range that is supported by the Wasm host.
 48#[inline(always)]
 49pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
 50    let max_version = if release_channel == ReleaseChannel::Dev {
 51        latest::MAX_VERSION
 52    } else {
 53        since_v0_0_6::MAX_VERSION
 54    };
 55
 56    since_v0_0_1::MIN_VERSION..=max_version
 57}
 58
 59pub enum Extension {
 60    V007(since_v0_0_7::Extension),
 61    V006(since_v0_0_6::Extension),
 62    V004(since_v0_0_4::Extension),
 63    V001(since_v0_0_1::Extension),
 64}
 65
 66impl Extension {
 67    pub async fn instantiate_async(
 68        store: &mut Store<WasmState>,
 69        release_channel: ReleaseChannel,
 70        version: SemanticVersion,
 71        component: &Component,
 72    ) -> Result<(Self, Instance)> {
 73        if release_channel == ReleaseChannel::Dev && version >= latest::MIN_VERSION {
 74            let (extension, instance) =
 75                latest::Extension::instantiate_async(store, &component, latest::linker())
 76                    .await
 77                    .context("failed to instantiate wasm extension")?;
 78            Ok((Self::V007(extension), instance))
 79        } else if version >= since_v0_0_6::MIN_VERSION {
 80            let (extension, instance) = since_v0_0_6::Extension::instantiate_async(
 81                store,
 82                &component,
 83                since_v0_0_6::linker(),
 84            )
 85            .await
 86            .context("failed to instantiate wasm extension")?;
 87            Ok((Self::V006(extension), instance))
 88        } else if version >= since_v0_0_4::MIN_VERSION {
 89            let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
 90                store,
 91                &component,
 92                since_v0_0_4::linker(),
 93            )
 94            .await
 95            .context("failed to instantiate wasm extension")?;
 96            Ok((Self::V004(extension), instance))
 97        } else {
 98            let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
 99                store,
100                &component,
101                since_v0_0_1::linker(),
102            )
103            .await
104            .context("failed to instantiate wasm extension")?;
105            Ok((Self::V001(extension), instance))
106        }
107    }
108
109    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
110        match self {
111            Extension::V007(ext) => ext.call_init_extension(store).await,
112            Extension::V006(ext) => ext.call_init_extension(store).await,
113            Extension::V004(ext) => ext.call_init_extension(store).await,
114            Extension::V001(ext) => ext.call_init_extension(store).await,
115        }
116    }
117
118    pub async fn call_language_server_command(
119        &self,
120        store: &mut Store<WasmState>,
121        language_server_id: &LanguageServerName,
122        config: &LanguageServerConfig,
123        resource: Resource<Arc<dyn LspAdapterDelegate>>,
124    ) -> Result<Result<Command, String>> {
125        match self {
126            Extension::V007(ext) => {
127                ext.call_language_server_command(store, &language_server_id.0, resource)
128                    .await
129            }
130            Extension::V006(ext) => Ok(ext
131                .call_language_server_command(store, &language_server_id.0, resource)
132                .await?
133                .map(|command| command.into())),
134            Extension::V004(ext) => Ok(ext
135                .call_language_server_command(store, config, resource)
136                .await?
137                .map(|command| command.into())),
138            Extension::V001(ext) => Ok(ext
139                .call_language_server_command(store, &config.clone().into(), resource)
140                .await?
141                .map(|command| command.into())),
142        }
143    }
144
145    pub async fn call_language_server_initialization_options(
146        &self,
147        store: &mut Store<WasmState>,
148        language_server_id: &LanguageServerName,
149        config: &LanguageServerConfig,
150        resource: Resource<Arc<dyn LspAdapterDelegate>>,
151    ) -> Result<Result<Option<String>, String>> {
152        match self {
153            Extension::V007(ext) => {
154                ext.call_language_server_initialization_options(
155                    store,
156                    &language_server_id.0,
157                    resource,
158                )
159                .await
160            }
161            Extension::V006(ext) => {
162                ext.call_language_server_initialization_options(
163                    store,
164                    &language_server_id.0,
165                    resource,
166                )
167                .await
168            }
169            Extension::V004(ext) => {
170                ext.call_language_server_initialization_options(store, config, resource)
171                    .await
172            }
173            Extension::V001(ext) => {
174                ext.call_language_server_initialization_options(
175                    store,
176                    &config.clone().into(),
177                    resource,
178                )
179                .await
180            }
181        }
182    }
183
184    pub async fn call_language_server_workspace_configuration(
185        &self,
186        store: &mut Store<WasmState>,
187        language_server_id: &LanguageServerName,
188        resource: Resource<Arc<dyn LspAdapterDelegate>>,
189    ) -> Result<Result<Option<String>, String>> {
190        match self {
191            Extension::V007(ext) => {
192                ext.call_language_server_workspace_configuration(
193                    store,
194                    &language_server_id.0,
195                    resource,
196                )
197                .await
198            }
199            Extension::V006(ext) => {
200                ext.call_language_server_workspace_configuration(
201                    store,
202                    &language_server_id.0,
203                    resource,
204                )
205                .await
206            }
207            Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
208        }
209    }
210
211    pub async fn call_labels_for_completions(
212        &self,
213        store: &mut Store<WasmState>,
214        language_server_id: &LanguageServerName,
215        completions: Vec<latest::Completion>,
216    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
217        match self {
218            Extension::V007(ext) => {
219                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
220                    .await
221            }
222            Extension::V006(ext) => Ok(ext
223                .call_labels_for_completions(store, &language_server_id.0, &completions)
224                .await?
225                .map(|labels| {
226                    labels
227                        .into_iter()
228                        .map(|label| label.map(Into::into))
229                        .collect()
230                })),
231            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
232        }
233    }
234
235    pub async fn call_labels_for_symbols(
236        &self,
237        store: &mut Store<WasmState>,
238        language_server_id: &LanguageServerName,
239        symbols: Vec<latest::Symbol>,
240    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
241        match self {
242            Extension::V007(ext) => {
243                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
244                    .await
245            }
246            Extension::V006(ext) => Ok(ext
247                .call_labels_for_symbols(store, &language_server_id.0, &symbols)
248                .await?
249                .map(|labels| {
250                    labels
251                        .into_iter()
252                        .map(|label| label.map(Into::into))
253                        .collect()
254                })),
255            Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
256        }
257    }
258
259    pub async fn call_run_slash_command(
260        &self,
261        store: &mut Store<WasmState>,
262        command: &SlashCommand,
263        argument: Option<&str>,
264        resource: Resource<Arc<dyn LspAdapterDelegate>>,
265    ) -> Result<Result<Option<String>, String>> {
266        match self {
267            Extension::V007(ext) => {
268                ext.call_run_slash_command(store, command, argument, resource)
269                    .await
270            }
271            Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(None)),
272        }
273    }
274}
275
276trait ToWasmtimeResult<T> {
277    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
278}
279
280impl<T> ToWasmtimeResult<T> for Result<T> {
281    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
282        Ok(self.map_err(|error| error.to_string()))
283    }
284}