1use crate::wasm_host::WasmState;
2use anyhow::Result;
3use extension::{KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate};
4use gpui::BackgroundExecutor;
5use semver::Version;
6use std::sync::{Arc, OnceLock};
7use wasmtime::component::{Linker, Resource};
8
9use super::latest;
10
11pub const MIN_VERSION: Version = Version::new(0, 6, 0);
12pub const MAX_VERSION: Version = Version::new(0, 7, 0);
13
14wasmtime::component::bindgen!({
15 async: true,
16 trappable_imports: true,
17 path: "../extension_api/wit/since_v0.6.0",
18 with: {
19 "worktree": ExtensionWorktree,
20 "project": ExtensionProject,
21 "key-value-store": ExtensionKeyValueStore,
22 "zed:extension/common": latest::zed::extension::common,
23 "zed:extension/http-client": latest::zed::extension::http_client,
24 "zed:extension/nodejs": latest::zed::extension::nodejs,
25 "zed:extension/platform": latest::zed::extension::platform,
26 "zed:extension/process": latest::zed::extension::process,
27 "zed:extension/slash-command": latest::zed::extension::slash_command,
28 "zed:extension/context-server": latest::zed::extension::context_server,
29 "zed:extension/dap": latest::zed::extension::dap,
30 },
31});
32
33pub use self::zed::extension::*;
34
35mod settings {
36 #![allow(dead_code)]
37 include!(concat!(env!("OUT_DIR"), "/since_v0.6.0/settings.rs"));
38}
39
40pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
41pub type ExtensionProject = Arc<dyn ProjectDelegate>;
42pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
43
44pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
45 static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
46 LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker))
47}
48
49impl From<CodeLabel> for latest::CodeLabel {
50 fn from(value: CodeLabel) -> Self {
51 Self {
52 code: value.code,
53 spans: value.spans.into_iter().map(Into::into).collect(),
54 filter_range: value.filter_range,
55 }
56 }
57}
58
59impl From<CodeLabelSpan> for latest::CodeLabelSpan {
60 fn from(value: CodeLabelSpan) -> Self {
61 match value {
62 CodeLabelSpan::CodeRange(range) => Self::CodeRange(range),
63 CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()),
64 }
65 }
66}
67
68impl From<CodeLabelSpanLiteral> for latest::CodeLabelSpanLiteral {
69 fn from(value: CodeLabelSpanLiteral) -> Self {
70 Self {
71 text: value.text,
72 highlight_name: value.highlight_name,
73 }
74 }
75}
76
77impl From<SettingsLocation> for latest::SettingsLocation {
78 fn from(value: SettingsLocation) -> Self {
79 Self {
80 worktree_id: value.worktree_id,
81 path: value.path,
82 }
83 }
84}
85
86impl From<LanguageServerInstallationStatus> for latest::LanguageServerInstallationStatus {
87 fn from(value: LanguageServerInstallationStatus) -> Self {
88 match value {
89 LanguageServerInstallationStatus::None => Self::None,
90 LanguageServerInstallationStatus::Downloading => Self::Downloading,
91 LanguageServerInstallationStatus::CheckingForUpdate => Self::CheckingForUpdate,
92 LanguageServerInstallationStatus::Failed(message) => Self::Failed(message),
93 }
94 }
95}
96
97impl From<DownloadedFileType> for latest::DownloadedFileType {
98 fn from(value: DownloadedFileType) -> Self {
99 match value {
100 DownloadedFileType::Gzip => Self::Gzip,
101 DownloadedFileType::GzipTar => Self::GzipTar,
102 DownloadedFileType::Zip => Self::Zip,
103 DownloadedFileType::Uncompressed => Self::Uncompressed,
104 }
105 }
106}
107
108impl From<latest::github::GithubReleaseAsset> for github::GithubReleaseAsset {
109 fn from(value: latest::github::GithubReleaseAsset) -> Self {
110 Self {
111 name: value.name,
112 download_url: value.download_url,
113 }
114 }
115}
116
117impl From<latest::github::GithubRelease> for github::GithubRelease {
118 fn from(value: latest::github::GithubRelease) -> Self {
119 Self {
120 version: value.version,
121 assets: value.assets.into_iter().map(Into::into).collect(),
122 }
123 }
124}
125
126impl From<github::GithubReleaseOptions> for latest::github::GithubReleaseOptions {
127 fn from(value: github::GithubReleaseOptions) -> Self {
128 Self {
129 require_assets: value.require_assets,
130 pre_release: value.pre_release,
131 }
132 }
133}
134
135impl zed::extension::github::Host for WasmState {
136 async fn github_release_by_tag_name(
137 &mut self,
138 repo: String,
139 tag: String,
140 ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
141 latest::github::Host::github_release_by_tag_name(self, repo, tag)
142 .await
143 .map(|result| result.map(Into::into))
144 }
145
146 async fn latest_github_release(
147 &mut self,
148 repo: String,
149 options: github::GithubReleaseOptions,
150 ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
151 latest::github::Host::latest_github_release(self, repo, options.into())
152 .await
153 .map(|result| result.map(Into::into))
154 }
155}
156
157impl From<latest::lsp::Completion> for lsp::Completion {
158 fn from(value: latest::lsp::Completion) -> Self {
159 Self {
160 label: value.label,
161 label_details: value.label_details.map(Into::into),
162 detail: value.detail,
163 kind: value.kind.map(Into::into),
164 insert_text_format: value.insert_text_format.map(Into::into),
165 }
166 }
167}
168
169impl From<latest::lsp::Symbol> for lsp::Symbol {
170 fn from(value: latest::lsp::Symbol) -> Self {
171 Self {
172 name: value.name,
173 kind: value.kind.into(),
174 }
175 }
176}
177
178impl From<latest::lsp::CompletionLabelDetails> for lsp::CompletionLabelDetails {
179 fn from(value: latest::lsp::CompletionLabelDetails) -> Self {
180 Self {
181 detail: value.detail,
182 description: value.description,
183 }
184 }
185}
186
187impl From<latest::lsp::CompletionKind> for lsp::CompletionKind {
188 fn from(value: latest::lsp::CompletionKind) -> Self {
189 match value {
190 latest::lsp::CompletionKind::Text => Self::Text,
191 latest::lsp::CompletionKind::Method => Self::Method,
192 latest::lsp::CompletionKind::Function => Self::Function,
193 latest::lsp::CompletionKind::Constructor => Self::Constructor,
194 latest::lsp::CompletionKind::Field => Self::Field,
195 latest::lsp::CompletionKind::Variable => Self::Variable,
196 latest::lsp::CompletionKind::Class => Self::Class,
197 latest::lsp::CompletionKind::Interface => Self::Interface,
198 latest::lsp::CompletionKind::Module => Self::Module,
199 latest::lsp::CompletionKind::Property => Self::Property,
200 latest::lsp::CompletionKind::Unit => Self::Unit,
201 latest::lsp::CompletionKind::Value => Self::Value,
202 latest::lsp::CompletionKind::Enum => Self::Enum,
203 latest::lsp::CompletionKind::Keyword => Self::Keyword,
204 latest::lsp::CompletionKind::Snippet => Self::Snippet,
205 latest::lsp::CompletionKind::Color => Self::Color,
206 latest::lsp::CompletionKind::File => Self::File,
207 latest::lsp::CompletionKind::Reference => Self::Reference,
208 latest::lsp::CompletionKind::Folder => Self::Folder,
209 latest::lsp::CompletionKind::EnumMember => Self::EnumMember,
210 latest::lsp::CompletionKind::Constant => Self::Constant,
211 latest::lsp::CompletionKind::Struct => Self::Struct,
212 latest::lsp::CompletionKind::Event => Self::Event,
213 latest::lsp::CompletionKind::Operator => Self::Operator,
214 latest::lsp::CompletionKind::TypeParameter => Self::TypeParameter,
215 latest::lsp::CompletionKind::Other(kind) => Self::Other(kind),
216 }
217 }
218}
219
220impl From<latest::lsp::InsertTextFormat> for lsp::InsertTextFormat {
221 fn from(value: latest::lsp::InsertTextFormat) -> Self {
222 match value {
223 latest::lsp::InsertTextFormat::PlainText => Self::PlainText,
224 latest::lsp::InsertTextFormat::Snippet => Self::Snippet,
225 latest::lsp::InsertTextFormat::Other(value) => Self::Other(value),
226 }
227 }
228}
229
230impl From<latest::lsp::SymbolKind> for lsp::SymbolKind {
231 fn from(value: latest::lsp::SymbolKind) -> Self {
232 match value {
233 latest::lsp::SymbolKind::File => Self::File,
234 latest::lsp::SymbolKind::Module => Self::Module,
235 latest::lsp::SymbolKind::Namespace => Self::Namespace,
236 latest::lsp::SymbolKind::Package => Self::Package,
237 latest::lsp::SymbolKind::Class => Self::Class,
238 latest::lsp::SymbolKind::Method => Self::Method,
239 latest::lsp::SymbolKind::Property => Self::Property,
240 latest::lsp::SymbolKind::Field => Self::Field,
241 latest::lsp::SymbolKind::Constructor => Self::Constructor,
242 latest::lsp::SymbolKind::Enum => Self::Enum,
243 latest::lsp::SymbolKind::Interface => Self::Interface,
244 latest::lsp::SymbolKind::Function => Self::Function,
245 latest::lsp::SymbolKind::Variable => Self::Variable,
246 latest::lsp::SymbolKind::Constant => Self::Constant,
247 latest::lsp::SymbolKind::String => Self::String,
248 latest::lsp::SymbolKind::Number => Self::Number,
249 latest::lsp::SymbolKind::Boolean => Self::Boolean,
250 latest::lsp::SymbolKind::Array => Self::Array,
251 latest::lsp::SymbolKind::Object => Self::Object,
252 latest::lsp::SymbolKind::Key => Self::Key,
253 latest::lsp::SymbolKind::Null => Self::Null,
254 latest::lsp::SymbolKind::EnumMember => Self::EnumMember,
255 latest::lsp::SymbolKind::Struct => Self::Struct,
256 latest::lsp::SymbolKind::Event => Self::Event,
257 latest::lsp::SymbolKind::Operator => Self::Operator,
258 latest::lsp::SymbolKind::TypeParameter => Self::TypeParameter,
259 latest::lsp::SymbolKind::Other(kind) => Self::Other(kind),
260 }
261 }
262}
263
264impl lsp::Host for WasmState {}
265
266impl HostKeyValueStore for WasmState {
267 async fn insert(
268 &mut self,
269 kv_store: Resource<ExtensionKeyValueStore>,
270 key: String,
271 value: String,
272 ) -> wasmtime::Result<Result<(), String>> {
273 latest::HostKeyValueStore::insert(self, kv_store, key, value).await
274 }
275
276 async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
277 // We only ever hand out borrows of key-value stores.
278 Ok(())
279 }
280}
281
282impl HostProject for WasmState {
283 async fn worktree_ids(
284 &mut self,
285 project: Resource<ExtensionProject>,
286 ) -> wasmtime::Result<Vec<u64>> {
287 latest::HostProject::worktree_ids(self, project).await
288 }
289
290 async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
291 // We only ever hand out borrows of projects.
292 Ok(())
293 }
294}
295
296impl HostWorktree for WasmState {
297 async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
298 latest::HostWorktree::id(self, delegate).await
299 }
300
301 async fn root_path(
302 &mut self,
303 delegate: Resource<Arc<dyn WorktreeDelegate>>,
304 ) -> wasmtime::Result<String> {
305 latest::HostWorktree::root_path(self, delegate).await
306 }
307
308 async fn read_text_file(
309 &mut self,
310 delegate: Resource<Arc<dyn WorktreeDelegate>>,
311 path: String,
312 ) -> wasmtime::Result<Result<String, String>> {
313 latest::HostWorktree::read_text_file(self, delegate, path).await
314 }
315
316 async fn shell_env(
317 &mut self,
318 delegate: Resource<Arc<dyn WorktreeDelegate>>,
319 ) -> wasmtime::Result<EnvVars> {
320 latest::HostWorktree::shell_env(self, delegate).await
321 }
322
323 async fn which(
324 &mut self,
325 delegate: Resource<Arc<dyn WorktreeDelegate>>,
326 binary_name: String,
327 ) -> wasmtime::Result<Option<String>> {
328 latest::HostWorktree::which(self, delegate, binary_name).await
329 }
330
331 async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
332 // We only ever hand out borrows of worktrees.
333 Ok(())
334 }
335}
336
337impl ExtensionImports for WasmState {
338 async fn get_settings(
339 &mut self,
340 location: Option<self::SettingsLocation>,
341 category: String,
342 key: Option<String>,
343 ) -> wasmtime::Result<Result<String, String>> {
344 latest::ExtensionImports::get_settings(
345 self,
346 location.map(|location| location.into()),
347 category,
348 key,
349 )
350 .await
351 }
352
353 async fn set_language_server_installation_status(
354 &mut self,
355 server_name: String,
356 status: LanguageServerInstallationStatus,
357 ) -> wasmtime::Result<()> {
358 latest::ExtensionImports::set_language_server_installation_status(
359 self,
360 server_name,
361 status.into(),
362 )
363 .await
364 }
365
366 async fn download_file(
367 &mut self,
368 url: String,
369 path: String,
370 file_type: DownloadedFileType,
371 ) -> wasmtime::Result<Result<(), String>> {
372 latest::ExtensionImports::download_file(self, url, path, file_type.into()).await
373 }
374
375 async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
376 latest::ExtensionImports::make_file_executable(self, path).await
377 }
378}