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::{
24 Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
25 },
26 zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
27 CodeLabel, CodeLabelSpan, Command, 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 => cfg!(test),
87 };
88 if !allow_latest_version {
89 Err(anyhow!(
90 "unreleased versions of the extension API can only be used on development builds of Zed"
91 ))?;
92 }
93 let extension =
94 latest::Extension::instantiate_async(store, component, latest::linker())
95 .await
96 .context("failed to instantiate wasm extension")?;
97 Ok(Self::V020(extension))
98 } else if version >= since_v0_1_0::MIN_VERSION {
99 let extension = since_v0_1_0::Extension::instantiate_async(
100 store,
101 component,
102 since_v0_1_0::linker(),
103 )
104 .await
105 .context("failed to instantiate wasm extension")?;
106 Ok(Self::V010(extension))
107 } else if version >= since_v0_0_6::MIN_VERSION {
108 let extension = since_v0_0_6::Extension::instantiate_async(
109 store,
110 component,
111 since_v0_0_6::linker(),
112 )
113 .await
114 .context("failed to instantiate wasm extension")?;
115 Ok(Self::V006(extension))
116 } else if version >= since_v0_0_4::MIN_VERSION {
117 let extension = since_v0_0_4::Extension::instantiate_async(
118 store,
119 component,
120 since_v0_0_4::linker(),
121 )
122 .await
123 .context("failed to instantiate wasm extension")?;
124 Ok(Self::V004(extension))
125 } else {
126 let extension = since_v0_0_1::Extension::instantiate_async(
127 store,
128 component,
129 since_v0_0_1::linker(),
130 )
131 .await
132 .context("failed to instantiate wasm extension")?;
133 Ok(Self::V001(extension))
134 }
135 }
136
137 pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
138 match self {
139 Extension::V020(ext) => ext.call_init_extension(store).await,
140 Extension::V010(ext) => ext.call_init_extension(store).await,
141 Extension::V006(ext) => ext.call_init_extension(store).await,
142 Extension::V004(ext) => ext.call_init_extension(store).await,
143 Extension::V001(ext) => ext.call_init_extension(store).await,
144 }
145 }
146
147 pub async fn call_language_server_command(
148 &self,
149 store: &mut Store<WasmState>,
150 language_server_id: &LanguageServerName,
151 config: &LanguageServerConfig,
152 resource: Resource<Arc<dyn LspAdapterDelegate>>,
153 ) -> Result<Result<Command, String>> {
154 match self {
155 Extension::V020(ext) => {
156 ext.call_language_server_command(store, &language_server_id.0, resource)
157 .await
158 }
159 Extension::V010(ext) => Ok(ext
160 .call_language_server_command(store, &language_server_id.0, resource)
161 .await?
162 .map(|command| command.into())),
163 Extension::V006(ext) => Ok(ext
164 .call_language_server_command(store, &language_server_id.0, resource)
165 .await?
166 .map(|command| command.into())),
167 Extension::V004(ext) => Ok(ext
168 .call_language_server_command(store, config, resource)
169 .await?
170 .map(|command| command.into())),
171 Extension::V001(ext) => Ok(ext
172 .call_language_server_command(store, &config.clone().into(), resource)
173 .await?
174 .map(|command| command.into())),
175 }
176 }
177
178 pub async fn call_language_server_initialization_options(
179 &self,
180 store: &mut Store<WasmState>,
181 language_server_id: &LanguageServerName,
182 config: &LanguageServerConfig,
183 resource: Resource<Arc<dyn LspAdapterDelegate>>,
184 ) -> Result<Result<Option<String>, String>> {
185 match self {
186 Extension::V020(ext) => {
187 ext.call_language_server_initialization_options(
188 store,
189 &language_server_id.0,
190 resource,
191 )
192 .await
193 }
194 Extension::V010(ext) => {
195 ext.call_language_server_initialization_options(
196 store,
197 &language_server_id.0,
198 resource,
199 )
200 .await
201 }
202 Extension::V006(ext) => {
203 ext.call_language_server_initialization_options(
204 store,
205 &language_server_id.0,
206 resource,
207 )
208 .await
209 }
210 Extension::V004(ext) => {
211 ext.call_language_server_initialization_options(store, config, resource)
212 .await
213 }
214 Extension::V001(ext) => {
215 ext.call_language_server_initialization_options(
216 store,
217 &config.clone().into(),
218 resource,
219 )
220 .await
221 }
222 }
223 }
224
225 pub async fn call_language_server_workspace_configuration(
226 &self,
227 store: &mut Store<WasmState>,
228 language_server_id: &LanguageServerName,
229 resource: Resource<Arc<dyn LspAdapterDelegate>>,
230 ) -> Result<Result<Option<String>, String>> {
231 match self {
232 Extension::V020(ext) => {
233 ext.call_language_server_workspace_configuration(
234 store,
235 &language_server_id.0,
236 resource,
237 )
238 .await
239 }
240 Extension::V010(ext) => {
241 ext.call_language_server_workspace_configuration(
242 store,
243 &language_server_id.0,
244 resource,
245 )
246 .await
247 }
248 Extension::V006(ext) => {
249 ext.call_language_server_workspace_configuration(
250 store,
251 &language_server_id.0,
252 resource,
253 )
254 .await
255 }
256 Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
257 }
258 }
259
260 pub async fn call_labels_for_completions(
261 &self,
262 store: &mut Store<WasmState>,
263 language_server_id: &LanguageServerName,
264 completions: Vec<latest::Completion>,
265 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
266 match self {
267 Extension::V020(ext) => {
268 ext.call_labels_for_completions(store, &language_server_id.0, &completions)
269 .await
270 }
271 Extension::V010(ext) => Ok(ext
272 .call_labels_for_completions(
273 store,
274 &language_server_id.0,
275 &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
276 )
277 .await?
278 .map(|labels| {
279 labels
280 .into_iter()
281 .map(|label| label.map(Into::into))
282 .collect()
283 })),
284 Extension::V006(ext) => Ok(ext
285 .call_labels_for_completions(
286 store,
287 &language_server_id.0,
288 &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
289 )
290 .await?
291 .map(|labels| {
292 labels
293 .into_iter()
294 .map(|label| label.map(Into::into))
295 .collect()
296 })),
297 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
298 }
299 }
300
301 pub async fn call_labels_for_symbols(
302 &self,
303 store: &mut Store<WasmState>,
304 language_server_id: &LanguageServerName,
305 symbols: Vec<latest::Symbol>,
306 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
307 match self {
308 Extension::V020(ext) => {
309 ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
310 .await
311 }
312 Extension::V010(ext) => Ok(ext
313 .call_labels_for_symbols(
314 store,
315 &language_server_id.0,
316 &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
317 )
318 .await?
319 .map(|labels| {
320 labels
321 .into_iter()
322 .map(|label| label.map(Into::into))
323 .collect()
324 })),
325 Extension::V006(ext) => Ok(ext
326 .call_labels_for_symbols(
327 store,
328 &language_server_id.0,
329 &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
330 )
331 .await?
332 .map(|labels| {
333 labels
334 .into_iter()
335 .map(|label| label.map(Into::into))
336 .collect()
337 })),
338 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
339 }
340 }
341
342 pub async fn call_complete_slash_command_argument(
343 &self,
344 store: &mut Store<WasmState>,
345 command: &SlashCommand,
346 arguments: &[String],
347 ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
348 match self {
349 Extension::V020(ext) => {
350 ext.call_complete_slash_command_argument(store, command, arguments)
351 .await
352 }
353 Extension::V010(ext) => {
354 ext.call_complete_slash_command_argument(store, command, arguments)
355 .await
356 }
357 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
358 }
359 }
360
361 pub async fn call_run_slash_command(
362 &self,
363 store: &mut Store<WasmState>,
364 command: &SlashCommand,
365 arguments: &[String],
366 resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
367 ) -> Result<Result<SlashCommandOutput, String>> {
368 match self {
369 Extension::V020(ext) => {
370 ext.call_run_slash_command(store, command, arguments, resource)
371 .await
372 }
373 Extension::V010(ext) => {
374 ext.call_run_slash_command(store, command, arguments, resource)
375 .await
376 }
377 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
378 Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
379 }
380 }
381 }
382
383 pub async fn call_suggest_docs_packages(
384 &self,
385 store: &mut Store<WasmState>,
386 provider: &str,
387 ) -> Result<Result<Vec<String>, String>> {
388 match self {
389 Extension::V020(ext) => ext.call_suggest_docs_packages(store, provider).await,
390 Extension::V010(ext) => ext.call_suggest_docs_packages(store, provider).await,
391 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Err(anyhow!(
392 "`suggest_docs_packages` not available prior to v0.1.0"
393 )),
394 }
395 }
396
397 pub async fn call_index_docs(
398 &self,
399 store: &mut Store<WasmState>,
400 provider: &str,
401 package_name: &str,
402 database: Resource<Arc<IndexedDocsDatabase>>,
403 ) -> Result<Result<(), String>> {
404 match self {
405 Extension::V020(ext) => {
406 ext.call_index_docs(store, provider, package_name, database)
407 .await
408 }
409 Extension::V010(ext) => {
410 ext.call_index_docs(store, provider, package_name, database)
411 .await
412 }
413 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
414 Err(anyhow!("`index_docs` not available prior to v0.1.0"))
415 }
416 }
417 }
418}
419
420trait ToWasmtimeResult<T> {
421 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
422}
423
424impl<T> ToWasmtimeResult<T> for Result<T> {
425 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
426 Ok(self.map_err(|error| error.to_string()))
427 }
428}