Skip to content

Commit 42e2379

Browse files
authored
Auto download Lombok if enabled (#29)
Auto downloads the latest version of Lombok (if it is enabled) in a similar fashion to when the extension auto downloads JDTLS. Closes #28.
1 parent fe1da00 commit 42e2379

File tree

2 files changed

+86
-55
lines changed

2 files changed

+86
-55
lines changed

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,14 @@ This extension adds support for the Java language.
99
You can optionally configure the class path that [JDTLS] uses in your Zed
1010
settings.
1111

12-
If [Lombok] support is enabled via [JDTLS] initialization option
13-
(`initialization_options.settings.java.jdt.ls.lombokSupport.enabled`), this
14-
extension will add [Lombok] as a javaagent to the JVM arguments for [JDTLS].
15-
You can also configure the version of [Lombok] to use via setting the version
16-
at `settings.lombok_version`.
17-
1812
Below is a configuration example for this extension:
1913

2014
```jsonc
2115
{
2216
"lsp": {
2317
"jdtls": {
2418
"settings": {
25-
"classpath": "/path/to/classes.jar:/path/to/more/classes/",
26-
"lombok_version": "1.18.34" // Defaults to latest version if not set
19+
"classpath": "/path/to/classes.jar:/path/to/more/classes/"
2720
}
2821
}
2922
}
@@ -32,6 +25,11 @@ Below is a configuration example for this extension:
3225

3326
### Initialization Options
3427

28+
If [Lombok] support is enabled via [JDTLS] initialization option
29+
(`initialization_options.settings.java.jdt.ls.lombokSupport.enabled`), this
30+
extension will download and add [Lombok] as a javaagent to the JVM arguments for
31+
[JDTLS].
32+
3533
There are also many more options you can pass directly to the language server,
3634
for example:
3735

src/lib.rs

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fs;
1+
use std::fs::{self, create_dir};
22

33
use zed_extension_api::{
44
self as zed, current_platform, download_file,
@@ -138,59 +138,92 @@ impl Java {
138138
Ok(binary_path)
139139
}
140140

141-
fn lombok_jar_path(
142-
&mut self,
143-
language_server_id: &LanguageServerId,
144-
worktree: &Worktree,
145-
) -> zed::Result<String> {
146-
// Quickly return if the lombok path is already cached
147-
// Expect lombok path to be validated when setting the cache so no checking is done here
141+
fn lombok_jar_path(&mut self, language_server_id: &LanguageServerId) -> zed::Result<String> {
142+
// Use cached path if exists
143+
148144
if let Some(path) = &self.cached_lombok_path {
149-
return Ok(path.clone());
145+
if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
146+
return Ok(path.clone());
147+
}
150148
}
151149

152-
// Use lombok version specified in settings
153-
// Unspecified version (None here) defaults to the latest version
154-
let lombok_version = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?
155-
.settings
156-
.and_then(|settings| {
157-
settings
158-
.get("lombok_version")
159-
.and_then(|version| version.as_str())
160-
.map(|version_str| version_str.to_string())
150+
// Check for latest version
151+
152+
set_language_server_installation_status(
153+
language_server_id,
154+
&LanguageServerInstallationStatus::CheckingForUpdate,
155+
);
156+
157+
let tags_response_body = serde_json::from_slice::<Value>(
158+
&fetch(
159+
&HttpRequest::builder()
160+
.method(HttpMethod::Get)
161+
.url("https://api.github.com/repos/projectlombok/lombok/tags")
162+
.build()?,
163+
)
164+
.map_err(|err| format!("failed to fetch GitHub tags: {err}"))?
165+
.body,
166+
)
167+
.map_err(|err| format!("failed to deserialize GitHub tags response: {err}"))?;
168+
let latest_version = &tags_response_body
169+
.as_array()
170+
.and_then(|tag| {
171+
tag.first().and_then(|latest_tag| {
172+
latest_tag
173+
.get("name")
174+
.and_then(|tag_name| tag_name.as_str())
175+
})
161176
})
162-
.map(|version| version.trim().to_string());
163-
164-
// Download lombok jar
165-
// https://projectlombok.org/downloads/lombok.jar always points to the latest version
166-
// https://projectlombok.org/downloads/lombok-{version}.jar points to the specified version
167-
let (lombok_url, lombok_path) = match lombok_version {
168-
Some(v) => (
169-
format!("https://projectlombok.org/downloads/lombok-{v}.jar"),
170-
format!("lombok-{v}.jar"),
171-
),
172-
None => (
173-
"https://projectlombok.org/downloads/lombok.jar".to_string(),
174-
"lombok.jar".to_string(),
175-
),
176-
};
177-
// Do not download if lombok jar already exists
178-
if !std::fs::metadata(&lombok_path).map_or(false, |stat| stat.is_file()) {
177+
// Exclude 'v' at beginning
178+
.ok_or("malformed GitHub tags response")?[1..];
179+
let prefix = "lombok";
180+
let jar_name = format!("lombok-{latest_version}.jar");
181+
let jar_path = format!("{prefix}/{jar_name}");
182+
183+
// If latest version isn't installed,
184+
if !fs::metadata(&jar_path).map_or(false, |stat| stat.is_file()) {
185+
// then download it...
186+
179187
set_language_server_installation_status(
180188
language_server_id,
181189
&LanguageServerInstallationStatus::Downloading,
182190
);
183-
download_file(&lombok_url, &lombok_path, DownloadedFileType::Uncompressed)
184-
.map_err(|e| format!("failed to download file from {lombok_url} : {e}"))
185-
.inspect_err(|e| {
186-
set_language_server_installation_status(
187-
language_server_id,
188-
&LanguageServerInstallationStatus::Failed(e.clone()),
189-
);
190-
})?;
191+
create_dir(prefix).map_err(|err| err.to_string())?;
192+
download_file(
193+
&format!("https://projectlombok.org/downloads/{jar_name}"),
194+
&jar_path,
195+
DownloadedFileType::Uncompressed,
196+
)?;
197+
198+
// ...and delete other versions
199+
200+
// This step is expected to fail sometimes, and since we don't know
201+
// how to fix it yet, we just carry on so the user doesn't have to
202+
// restart the language server.
203+
match fs::read_dir(prefix) {
204+
Ok(entries) => {
205+
for entry in entries {
206+
match entry {
207+
Ok(entry) => {
208+
if entry.file_name().to_str() != Some(&jar_name) {
209+
if let Err(err) = fs::remove_dir_all(entry.path()) {
210+
println!("failed to remove directory entry: {err}");
211+
}
212+
}
213+
}
214+
Err(err) => println!("failed to load directory entry: {err}"),
215+
}
216+
}
217+
}
218+
Err(err) => println!("failed to list prefix directory: {err}"),
219+
}
191220
}
192-
self.cached_lombok_path = Some(lombok_path.to_string());
193-
Ok(lombok_path.to_string())
221+
222+
// else use it
223+
224+
self.cached_lombok_path = Some(jar_path.clone());
225+
226+
Ok(jar_path)
194227
}
195228
}
196229

@@ -247,12 +280,12 @@ impl Extension for Java {
247280
.initialization_options
248281
.and_then(|initialization_options| {
249282
initialization_options
250-
.pointer("settings/java/jdt/ls/lombokSupport/enabled")
283+
.pointer("/settings/java/jdt/ls/lombokSupport/enabled")
251284
.and_then(|enabled| enabled.as_bool())
252285
})
253286
.unwrap_or(false);
254287
if lombok_enabled {
255-
let lombok_jar_path = self.lombok_jar_path(language_server_id, worktree)?;
288+
let lombok_jar_path = self.lombok_jar_path(language_server_id)?;
256289
let lombok_jar_full_path = std::env::current_dir()
257290
.map_err(|e| format!("could not get current dir: {e}"))?
258291
.join(&lombok_jar_path)

0 commit comments

Comments
 (0)