1use super::latest;
2use crate::wasm_host::WasmState;
3use anyhow::Result;
4use async_trait::async_trait;
5use language::{LanguageServerBinaryStatus, LspAdapterDelegate};
6use semantic_version::SemanticVersion;
7use std::sync::{Arc, OnceLock};
8use wasmtime::component::{Linker, Resource};
9
10pub const MIN_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 1);
11
12wasmtime::component::bindgen!({
13 async: true,
14 path: "../extension_api/wit/since_v0.0.1",
15 with: {
16 "worktree": ExtensionWorktree,
17 },
18});
19
20pub type ExtensionWorktree = Arc<dyn LspAdapterDelegate>;
21
22pub fn linker() -> &'static Linker<WasmState> {
23 static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
24 LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker))
25}
26
27impl From<latest::Os> for Os {
28 fn from(value: latest::Os) -> Self {
29 match value {
30 latest::Os::Mac => Os::Mac,
31 latest::Os::Linux => Os::Linux,
32 latest::Os::Windows => Os::Windows,
33 }
34 }
35}
36
37impl From<latest::Architecture> for Architecture {
38 fn from(value: latest::Architecture) -> Self {
39 match value {
40 latest::Architecture::Aarch64 => Self::Aarch64,
41 latest::Architecture::X86 => Self::X86,
42 latest::Architecture::X8664 => Self::X8664,
43 }
44 }
45}
46
47impl From<latest::GithubRelease> for GithubRelease {
48 fn from(value: latest::GithubRelease) -> Self {
49 Self {
50 version: value.version,
51 assets: value.assets.into_iter().map(|asset| asset.into()).collect(),
52 }
53 }
54}
55
56impl From<latest::GithubReleaseAsset> for GithubReleaseAsset {
57 fn from(value: latest::GithubReleaseAsset) -> Self {
58 Self {
59 name: value.name,
60 download_url: value.download_url,
61 }
62 }
63}
64
65impl From<GithubReleaseOptions> for latest::GithubReleaseOptions {
66 fn from(value: GithubReleaseOptions) -> Self {
67 Self {
68 require_assets: value.require_assets,
69 pre_release: value.pre_release,
70 }
71 }
72}
73
74impl From<DownloadedFileType> for latest::DownloadedFileType {
75 fn from(value: DownloadedFileType) -> Self {
76 match value {
77 DownloadedFileType::Gzip => latest::DownloadedFileType::Gzip,
78 DownloadedFileType::GzipTar => latest::DownloadedFileType::GzipTar,
79 DownloadedFileType::Zip => latest::DownloadedFileType::Zip,
80 DownloadedFileType::Uncompressed => latest::DownloadedFileType::Uncompressed,
81 }
82 }
83}
84
85impl From<latest::LanguageServerConfig> for LanguageServerConfig {
86 fn from(value: latest::LanguageServerConfig) -> Self {
87 Self {
88 name: value.name,
89 language_name: value.language_name,
90 }
91 }
92}
93
94impl From<Command> for latest::Command {
95 fn from(value: Command) -> Self {
96 Self {
97 command: value.command,
98 args: value.args,
99 env: value.env,
100 }
101 }
102}
103
104#[async_trait]
105impl HostWorktree for WasmState {
106 async fn read_text_file(
107 &mut self,
108 delegate: Resource<Arc<dyn LspAdapterDelegate>>,
109 path: String,
110 ) -> wasmtime::Result<Result<String, String>> {
111 latest::HostWorktree::read_text_file(self, delegate, path).await
112 }
113
114 async fn shell_env(
115 &mut self,
116 delegate: Resource<Arc<dyn LspAdapterDelegate>>,
117 ) -> wasmtime::Result<EnvVars> {
118 latest::HostWorktree::shell_env(self, delegate).await
119 }
120
121 async fn which(
122 &mut self,
123 delegate: Resource<Arc<dyn LspAdapterDelegate>>,
124 binary_name: String,
125 ) -> wasmtime::Result<Option<String>> {
126 latest::HostWorktree::which(self, delegate, binary_name).await
127 }
128
129 fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
130 Ok(())
131 }
132}
133
134#[async_trait]
135impl ExtensionImports for WasmState {
136 async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
137 latest::ExtensionImports::node_binary_path(self).await
138 }
139
140 async fn npm_package_latest_version(
141 &mut self,
142 package_name: String,
143 ) -> wasmtime::Result<Result<String, String>> {
144 latest::ExtensionImports::npm_package_latest_version(self, package_name).await
145 }
146
147 async fn npm_package_installed_version(
148 &mut self,
149 package_name: String,
150 ) -> wasmtime::Result<Result<Option<String>, String>> {
151 latest::ExtensionImports::npm_package_installed_version(self, package_name).await
152 }
153
154 async fn npm_install_package(
155 &mut self,
156 package_name: String,
157 version: String,
158 ) -> wasmtime::Result<Result<(), String>> {
159 latest::ExtensionImports::npm_install_package(self, package_name, version).await
160 }
161
162 async fn latest_github_release(
163 &mut self,
164 repo: String,
165 options: GithubReleaseOptions,
166 ) -> wasmtime::Result<Result<GithubRelease, String>> {
167 Ok(
168 latest::ExtensionImports::latest_github_release(self, repo, options.into())
169 .await?
170 .map(|github| github.into()),
171 )
172 }
173
174 async fn current_platform(&mut self) -> Result<(Os, Architecture)> {
175 latest::ExtensionImports::current_platform(self)
176 .await
177 .map(|(os, arch)| (os.into(), arch.into()))
178 }
179
180 async fn set_language_server_installation_status(
181 &mut self,
182 server_name: String,
183 status: LanguageServerInstallationStatus,
184 ) -> wasmtime::Result<()> {
185 let status = match status {
186 LanguageServerInstallationStatus::CheckingForUpdate => {
187 LanguageServerBinaryStatus::CheckingForUpdate
188 }
189 LanguageServerInstallationStatus::Downloading => {
190 LanguageServerBinaryStatus::Downloading
191 }
192 LanguageServerInstallationStatus::Cached
193 | LanguageServerInstallationStatus::Downloaded => LanguageServerBinaryStatus::None,
194 LanguageServerInstallationStatus::Failed(error) => {
195 LanguageServerBinaryStatus::Failed { error }
196 }
197 };
198
199 self.host
200 .language_registry
201 .update_lsp_status(language::LanguageServerName(server_name.into()), status);
202 Ok(())
203 }
204
205 async fn download_file(
206 &mut self,
207 url: String,
208 path: String,
209 file_type: DownloadedFileType,
210 ) -> wasmtime::Result<Result<(), String>> {
211 latest::ExtensionImports::download_file(self, url, path, file_type.into()).await
212 }
213}