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