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