1mod since_v0_0_1;
2mod since_v0_0_4;
3mod since_v0_0_6;
4mod since_v0_0_7;
5use release_channel::ReleaseChannel;
6use since_v0_0_7 as latest;
7
8use super::{wasm_engine, WasmState};
9use anyhow::{Context, Result};
10use language::{LanguageServerName, LspAdapterDelegate};
11use semantic_version::SemanticVersion;
12use std::{ops::RangeInclusive, sync::Arc};
13use wasmtime::{
14 component::{Component, Instance, Linker, Resource},
15 Store,
16};
17
18#[cfg(test)]
19pub use latest::CodeLabelSpanLiteral;
20pub use latest::{
21 zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
22 CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
23};
24pub use since_v0_0_4::LanguageServerConfig;
25
26pub fn new_linker(
27 f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
28) -> Linker<WasmState> {
29 let mut linker = Linker::new(&wasm_engine());
30 wasmtime_wasi::command::add_to_linker(&mut linker).unwrap();
31 f(&mut linker, wasi_view).unwrap();
32 linker
33}
34
35fn wasi_view(state: &mut WasmState) -> &mut WasmState {
36 state
37}
38
39/// Returns whether the given Wasm API version is supported by the Wasm host.
40pub fn is_supported_wasm_api_version(
41 release_channel: ReleaseChannel,
42 version: SemanticVersion,
43) -> bool {
44 wasm_api_version_range(release_channel).contains(&version)
45}
46
47/// Returns the Wasm API version range that is supported by the Wasm host.
48#[inline(always)]
49pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
50 let max_version = if release_channel == ReleaseChannel::Dev {
51 latest::MAX_VERSION
52 } else {
53 since_v0_0_6::MAX_VERSION
54 };
55
56 since_v0_0_1::MIN_VERSION..=max_version
57}
58
59pub enum Extension {
60 V007(since_v0_0_7::Extension),
61 V006(since_v0_0_6::Extension),
62 V004(since_v0_0_4::Extension),
63 V001(since_v0_0_1::Extension),
64}
65
66impl Extension {
67 pub async fn instantiate_async(
68 store: &mut Store<WasmState>,
69 release_channel: ReleaseChannel,
70 version: SemanticVersion,
71 component: &Component,
72 ) -> Result<(Self, Instance)> {
73 if release_channel == ReleaseChannel::Dev && version >= latest::MIN_VERSION {
74 let (extension, instance) =
75 latest::Extension::instantiate_async(store, &component, latest::linker())
76 .await
77 .context("failed to instantiate wasm extension")?;
78 Ok((Self::V007(extension), instance))
79 } else if version >= since_v0_0_6::MIN_VERSION {
80 let (extension, instance) = since_v0_0_6::Extension::instantiate_async(
81 store,
82 &component,
83 since_v0_0_6::linker(),
84 )
85 .await
86 .context("failed to instantiate wasm extension")?;
87 Ok((Self::V006(extension), instance))
88 } else if version >= since_v0_0_4::MIN_VERSION {
89 let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
90 store,
91 &component,
92 since_v0_0_4::linker(),
93 )
94 .await
95 .context("failed to instantiate wasm extension")?;
96 Ok((Self::V004(extension), instance))
97 } else {
98 let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
99 store,
100 &component,
101 since_v0_0_1::linker(),
102 )
103 .await
104 .context("failed to instantiate wasm extension")?;
105 Ok((Self::V001(extension), instance))
106 }
107 }
108
109 pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
110 match self {
111 Extension::V007(ext) => ext.call_init_extension(store).await,
112 Extension::V006(ext) => ext.call_init_extension(store).await,
113 Extension::V004(ext) => ext.call_init_extension(store).await,
114 Extension::V001(ext) => ext.call_init_extension(store).await,
115 }
116 }
117
118 pub async fn call_language_server_command(
119 &self,
120 store: &mut Store<WasmState>,
121 language_server_id: &LanguageServerName,
122 config: &LanguageServerConfig,
123 resource: Resource<Arc<dyn LspAdapterDelegate>>,
124 ) -> Result<Result<Command, String>> {
125 match self {
126 Extension::V007(ext) => {
127 ext.call_language_server_command(store, &language_server_id.0, resource)
128 .await
129 }
130 Extension::V006(ext) => Ok(ext
131 .call_language_server_command(store, &language_server_id.0, resource)
132 .await?
133 .map(|command| command.into())),
134 Extension::V004(ext) => Ok(ext
135 .call_language_server_command(store, config, resource)
136 .await?
137 .map(|command| command.into())),
138 Extension::V001(ext) => Ok(ext
139 .call_language_server_command(store, &config.clone().into(), resource)
140 .await?
141 .map(|command| command.into())),
142 }
143 }
144
145 pub async fn call_language_server_initialization_options(
146 &self,
147 store: &mut Store<WasmState>,
148 language_server_id: &LanguageServerName,
149 config: &LanguageServerConfig,
150 resource: Resource<Arc<dyn LspAdapterDelegate>>,
151 ) -> Result<Result<Option<String>, String>> {
152 match self {
153 Extension::V007(ext) => {
154 ext.call_language_server_initialization_options(
155 store,
156 &language_server_id.0,
157 resource,
158 )
159 .await
160 }
161 Extension::V006(ext) => {
162 ext.call_language_server_initialization_options(
163 store,
164 &language_server_id.0,
165 resource,
166 )
167 .await
168 }
169 Extension::V004(ext) => {
170 ext.call_language_server_initialization_options(store, config, resource)
171 .await
172 }
173 Extension::V001(ext) => {
174 ext.call_language_server_initialization_options(
175 store,
176 &config.clone().into(),
177 resource,
178 )
179 .await
180 }
181 }
182 }
183
184 pub async fn call_language_server_workspace_configuration(
185 &self,
186 store: &mut Store<WasmState>,
187 language_server_id: &LanguageServerName,
188 resource: Resource<Arc<dyn LspAdapterDelegate>>,
189 ) -> Result<Result<Option<String>, String>> {
190 match self {
191 Extension::V007(ext) => {
192 ext.call_language_server_workspace_configuration(
193 store,
194 &language_server_id.0,
195 resource,
196 )
197 .await
198 }
199 Extension::V006(ext) => {
200 ext.call_language_server_workspace_configuration(
201 store,
202 &language_server_id.0,
203 resource,
204 )
205 .await
206 }
207 Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
208 }
209 }
210
211 pub async fn call_labels_for_completions(
212 &self,
213 store: &mut Store<WasmState>,
214 language_server_id: &LanguageServerName,
215 completions: Vec<latest::Completion>,
216 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
217 match self {
218 Extension::V007(ext) => {
219 ext.call_labels_for_completions(store, &language_server_id.0, &completions)
220 .await
221 }
222 Extension::V006(ext) => Ok(ext
223 .call_labels_for_completions(store, &language_server_id.0, &completions)
224 .await?
225 .map(|labels| {
226 labels
227 .into_iter()
228 .map(|label| label.map(Into::into))
229 .collect()
230 })),
231 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
232 }
233 }
234
235 pub async fn call_labels_for_symbols(
236 &self,
237 store: &mut Store<WasmState>,
238 language_server_id: &LanguageServerName,
239 symbols: Vec<latest::Symbol>,
240 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
241 match self {
242 Extension::V007(ext) => {
243 ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
244 .await
245 }
246 Extension::V006(ext) => Ok(ext
247 .call_labels_for_symbols(store, &language_server_id.0, &symbols)
248 .await?
249 .map(|labels| {
250 labels
251 .into_iter()
252 .map(|label| label.map(Into::into))
253 .collect()
254 })),
255 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
256 }
257 }
258
259 pub async fn call_run_slash_command(
260 &self,
261 store: &mut Store<WasmState>,
262 command: &SlashCommand,
263 argument: Option<&str>,
264 resource: Resource<Arc<dyn LspAdapterDelegate>>,
265 ) -> Result<Result<Option<String>, String>> {
266 match self {
267 Extension::V007(ext) => {
268 ext.call_run_slash_command(store, command, argument, resource)
269 .await
270 }
271 Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(None)),
272 }
273 }
274}
275
276trait ToWasmtimeResult<T> {
277 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
278}
279
280impl<T> ToWasmtimeResult<T> for Result<T> {
281 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
282 Ok(self.map_err(|error| error.to_string()))
283 }
284}