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