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