1mod since_v0_0_1;
2mod since_v0_0_4;
3mod since_v0_0_6;
4use since_v0_0_6 as latest;
5
6use super::{wasm_engine, WasmState};
7use anyhow::{Context, Result};
8use language::{LanguageServerName, LspAdapterDelegate};
9use semantic_version::SemanticVersion;
10use std::{ops::RangeInclusive, sync::Arc};
11use wasmtime::{
12 component::{Component, Instance, Linker, Resource},
13 Store,
14};
15
16#[cfg(test)]
17pub use latest::CodeLabelSpanLiteral;
18pub use latest::{
19 zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
20 CodeLabel, CodeLabelSpan, Command, Range,
21};
22pub use since_v0_0_4::LanguageServerConfig;
23
24pub fn new_linker(
25 f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
26) -> Linker<WasmState> {
27 let mut linker = Linker::new(&wasm_engine());
28 wasmtime_wasi::command::add_to_linker(&mut linker).unwrap();
29 f(&mut linker, wasi_view).unwrap();
30 linker
31}
32
33fn wasi_view(state: &mut WasmState) -> &mut WasmState {
34 state
35}
36
37/// Returns whether the given Wasm API version is supported by the Wasm host.
38pub fn is_supported_wasm_api_version(version: SemanticVersion) -> bool {
39 wasm_api_version_range().contains(&version)
40}
41
42/// Returns the Wasm API version range that is supported by the Wasm host.
43#[inline(always)]
44pub fn wasm_api_version_range() -> RangeInclusive<SemanticVersion> {
45 since_v0_0_1::MIN_VERSION..=latest::MAX_VERSION
46}
47
48pub enum Extension {
49 V006(since_v0_0_6::Extension),
50 V004(since_v0_0_4::Extension),
51 V001(since_v0_0_1::Extension),
52}
53
54impl Extension {
55 pub async fn instantiate_async(
56 store: &mut Store<WasmState>,
57 version: SemanticVersion,
58 component: &Component,
59 ) -> Result<(Self, Instance)> {
60 if version >= latest::MIN_VERSION {
61 let (extension, instance) =
62 latest::Extension::instantiate_async(store, &component, latest::linker())
63 .await
64 .context("failed to instantiate wasm extension")?;
65 Ok((Self::V006(extension), instance))
66 } else if version >= since_v0_0_4::MIN_VERSION {
67 let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
68 store,
69 &component,
70 since_v0_0_4::linker(),
71 )
72 .await
73 .context("failed to instantiate wasm extension")?;
74 Ok((Self::V004(extension), instance))
75 } else {
76 let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
77 store,
78 &component,
79 since_v0_0_1::linker(),
80 )
81 .await
82 .context("failed to instantiate wasm extension")?;
83 Ok((Self::V001(extension), instance))
84 }
85 }
86
87 pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
88 match self {
89 Extension::V006(ext) => ext.call_init_extension(store).await,
90 Extension::V004(ext) => ext.call_init_extension(store).await,
91 Extension::V001(ext) => ext.call_init_extension(store).await,
92 }
93 }
94
95 pub async fn call_language_server_command(
96 &self,
97 store: &mut Store<WasmState>,
98 language_server_id: &LanguageServerName,
99 config: &LanguageServerConfig,
100 resource: Resource<Arc<dyn LspAdapterDelegate>>,
101 ) -> Result<Result<Command, String>> {
102 match self {
103 Extension::V006(ext) => {
104 ext.call_language_server_command(store, &language_server_id.0, resource)
105 .await
106 }
107 Extension::V004(ext) => Ok(ext
108 .call_language_server_command(store, config, resource)
109 .await?
110 .map(|command| command.into())),
111 Extension::V001(ext) => Ok(ext
112 .call_language_server_command(store, &config.clone().into(), resource)
113 .await?
114 .map(|command| command.into())),
115 }
116 }
117
118 pub async fn call_language_server_initialization_options(
119 &self,
120 store: &mut Store<WasmState>,
121 language_server_id: &LanguageServerName,
122 config: &LanguageServerConfig,
123 resource: Resource<Arc<dyn LspAdapterDelegate>>,
124 ) -> Result<Result<Option<String>, String>> {
125 match self {
126 Extension::V006(ext) => {
127 ext.call_language_server_initialization_options(
128 store,
129 &language_server_id.0,
130 resource,
131 )
132 .await
133 }
134 Extension::V004(ext) => {
135 ext.call_language_server_initialization_options(store, config, resource)
136 .await
137 }
138 Extension::V001(ext) => {
139 ext.call_language_server_initialization_options(
140 store,
141 &config.clone().into(),
142 resource,
143 )
144 .await
145 }
146 }
147 }
148
149 pub async fn call_language_server_workspace_configuration(
150 &self,
151 store: &mut Store<WasmState>,
152 language_server_id: &LanguageServerName,
153 resource: Resource<Arc<dyn LspAdapterDelegate>>,
154 ) -> Result<Result<Option<String>, String>> {
155 match self {
156 Extension::V006(ext) => {
157 ext.call_language_server_workspace_configuration(
158 store,
159 &language_server_id.0,
160 resource,
161 )
162 .await
163 }
164 Extension::V004(_) | Extension::V001(_) => Ok(Ok(None)),
165 }
166 }
167
168 pub async fn call_labels_for_completions(
169 &self,
170 store: &mut Store<WasmState>,
171 language_server_id: &LanguageServerName,
172 completions: Vec<latest::Completion>,
173 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
174 match self {
175 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
176 Extension::V006(ext) => {
177 ext.call_labels_for_completions(store, &language_server_id.0, &completions)
178 .await
179 }
180 }
181 }
182
183 pub async fn call_labels_for_symbols(
184 &self,
185 store: &mut Store<WasmState>,
186 language_server_id: &LanguageServerName,
187 symbols: Vec<latest::Symbol>,
188 ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
189 match self {
190 Extension::V001(_) | Extension::V004(_) => Ok(Ok(Vec::new())),
191 Extension::V006(ext) => {
192 ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
193 .await
194 }
195 }
196 }
197}
198
199trait ToWasmtimeResult<T> {
200 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
201}
202
203impl<T> ToWasmtimeResult<T> for Result<T> {
204 fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
205 Ok(self.map_err(|error| error.to_string()))
206 }
207}