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