Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 50 additions & 44 deletions src/solidlsp/language_servers/eclipse_jdtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import dataclasses
import glob
import hashlib
import logging
import os
Expand All @@ -26,21 +27,26 @@
log = logging.getLogger(__name__)

GRADLE_ALLOWED_HOSTS = ("services.gradle.org", "github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com")
DEFAULT_GRADLE_VERSION = "8.14.2"
GRADLE_SHA256 = "7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999"
VSCODE_JAVA_ALLOWED_HOSTS = ("github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com")
DEFAULT_VSCODE_JAVA_VERSION = "1.53.0-873"
VSCODE_JAVA_SHA256_BY_PLATFORM = {
"osx-arm64": "bc00c2699d4b8d478eb9a1621db9d6d3a12ea0dcc247a9cd8040e8ac19c03933",
"osx-x64": "03ae1db1a22c15561a620f1b722d6797d35d4faaa7c4666dbe6ca2715089852f",
"linux-arm64": "e15bc9b2a665d3453203402621b5441062aa41b0ec2d140661f439326fd248c1",
"linux-x64": "7660b7b527be6fda46a917966b34d828e7416d5cc84287b29b88e7b99c1737f9",
"win-x64": "ef195b45bd260976ad2e84618f4044b5d7248deed41d647573f0ee22c4233df3",
"osx-arm64": "3a9acf30b682df2f0b895728ad8f84725a95b326e2265f17bf9b087acb08dd0d",
"osx-x64": "73823bd3b0765bb9b483ba45216c229065df33e16a40623a7da5d92ae32e1471",
"linux-arm64": "92d42123b2282f970517a62cdb4ea2e5d7ffb255e665537f40641f6961a148fc",
"linux-x64": "24e9e605cd40b523fe62be350fe8550dafdaa6881010b2c595b26e425f2ee400",
"win-x64": "13aeda95e0494442a951f752c7334d97bb7a49991ae5a8b1a6cdb6a8dfcac128",
}
INTELLICODE_ALLOWED_HOSTS = (
"visualstudioexptteam.gallery.vsassets.io",
"marketplace.visualstudio.com",
"download.visualstudio.microsoft.com",
)
DEFAULT_INTELLICODE_VERSION = "1.2.30"
INTELLICODE_SHA256 = "7f61a7f96d101cdf230f96821be3fddd8f890ebfefb3695d18beee43004ae251"
DEFAULT_ECLIPSE_LAUNCHER_VERSION = "1.7.100.v20251111-0406"
DEFAULT_JRE_VERSION = "21.0.10"


@dataclasses.dataclass
Expand Down Expand Up @@ -85,7 +91,7 @@ class EclipseJDTLS(SolidLanguageServer):
gradle_java_home: "/path/to/jdk" # set to override Gradle's JDK
use_system_java_home: true # set to true to use system JAVA_HOME for JDTLS
gradle_version: "8.14.2"
vscode_java_version: "1.42.0-561"
vscode_java_version: "1.53.0-873"
intellicode_version: "1.2.30"
```
"""
Expand Down Expand Up @@ -148,21 +154,22 @@ def _setup_runtime_dependencies(
Setup runtime dependencies for EclipseJDTLS and return the paths.
"""
platformId = PlatformUtils.get_platform_id()
gradle_version = custom_settings.get("gradle_version", "8.14.2")
vscode_java_version = custom_settings.get("vscode_java_version", "1.42.0-561")
gradle_version = custom_settings.get("gradle_version", DEFAULT_GRADLE_VERSION)
vscode_java_version = custom_settings.get("vscode_java_version", DEFAULT_VSCODE_JAVA_VERSION)
vscode_java_tag = f"v{vscode_java_version.rsplit('-', 1)[0]}"
intellicode_version = custom_settings.get("intellicode_version", "1.2.30")
default_gradle_version = gradle_version == "8.14.2"
default_vscode_java_version = vscode_java_version == "1.42.0-561"
default_intellicode_version = intellicode_version == "1.2.30"
intellicode_version = custom_settings.get("intellicode_version", DEFAULT_INTELLICODE_VERSION)
eclipse_launcher_version = custom_settings.get("eclipse_launcher_version", DEFAULT_ECLIPSE_LAUNCHER_VERSION)
is_default_gradle_version = gradle_version == DEFAULT_GRADLE_VERSION
is_default_vscode_java_version = vscode_java_version == DEFAULT_VSCODE_JAVA_VERSION
is_default_intellicode_version = intellicode_version == DEFAULT_INTELLICODE_VERSION

runtime_dependencies: dict[str, dict[str, dict[str, object]]] = {
"gradle": {
"platform-agnostic": {
"url": f"https://services.gradle.org/distributions/gradle-{gradle_version}-bin.zip",
"archiveType": "zip",
"relative_extraction_path": ".",
"sha256": GRADLE_SHA256 if default_gradle_version else None,
"sha256": GRADLE_SHA256 if is_default_gradle_version else None,
"allowed_hosts": GRADLE_ALLOWED_HOSTS,
}
},
Expand All @@ -171,67 +178,62 @@ def _setup_runtime_dependencies(
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-darwin-arm64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-arm64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-arm64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
},
"osx-arm64": {
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-darwin-arm64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-arm64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-arm64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
"jre_home_path": "extension/jre/21.0.7-macosx-aarch64",
"jre_path": "extension/jre/21.0.7-macosx-aarch64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jre_home_path": f"extension/jre/{DEFAULT_JRE_VERSION}-macosx-aarch64",
"jre_path": f"extension/jre/{DEFAULT_JRE_VERSION}-macosx-aarch64/bin/java",
"jdtls_launcher_jar_path": f"extension/server/plugins/org.eclipse.equinox.launcher_{eclipse_launcher_version}.jar",
"jdtls_readonly_config_path": "extension/server/config_mac_arm",
},
"osx-x64": {
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-darwin-x64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-x64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["osx-x64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
"jre_home_path": "extension/jre/21.0.7-macosx-x86_64",
"jre_path": "extension/jre/21.0.7-macosx-x86_64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jre_home_path": f"extension/jre/{DEFAULT_JRE_VERSION}-macosx-x86_64",
"jre_path": f"extension/jre/{DEFAULT_JRE_VERSION}-macosx-x86_64/bin/java",
"jdtls_launcher_jar_path": f"extension/server/plugins/org.eclipse.equinox.launcher_{eclipse_launcher_version}.jar",
"jdtls_readonly_config_path": "extension/server/config_mac",
},
"linux-arm64": {
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-linux-arm64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["linux-arm64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["linux-arm64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
"jre_home_path": "extension/jre/21.0.7-linux-aarch64",
"jre_path": "extension/jre/21.0.7-linux-aarch64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jre_home_path": f"extension/jre/{DEFAULT_JRE_VERSION}-linux-aarch64",
"jre_path": f"extension/jre/{DEFAULT_JRE_VERSION}-linux-aarch64/bin/java",
"jdtls_launcher_jar_path": f"extension/server/plugins/org.eclipse.equinox.launcher_{eclipse_launcher_version}.jar",
"jdtls_readonly_config_path": "extension/server/config_linux_arm",
},
"linux-x64": {
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-linux-x64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["linux-x64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["linux-x64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
"jre_home_path": "extension/jre/21.0.7-linux-x86_64",
"jre_path": "extension/jre/21.0.7-linux-x86_64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jre_home_path": f"extension/jre/{DEFAULT_JRE_VERSION}-linux-x86_64",
"jre_path": f"extension/jre/{DEFAULT_JRE_VERSION}-linux-x86_64/bin/java",
"jdtls_launcher_jar_path": f"extension/server/plugins/org.eclipse.equinox.launcher_{eclipse_launcher_version}.jar",
"jdtls_readonly_config_path": "extension/server/config_linux",
},
"win-x64": {
"url": f"https://github.com/redhat-developer/vscode-java/releases/download/{vscode_java_tag}/java-win32-x64-{vscode_java_version}.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["win-x64"] if default_vscode_java_version else None,
"sha256": VSCODE_JAVA_SHA256_BY_PLATFORM["win-x64"] if is_default_vscode_java_version else None,
"allowed_hosts": VSCODE_JAVA_ALLOWED_HOSTS,
"jre_home_path": "extension/jre/21.0.7-win32-x86_64",
"jre_path": "extension/jre/21.0.7-win32-x86_64/bin/java.exe",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jre_home_path": f"extension/jre/{DEFAULT_JRE_VERSION}-win32-x86_64",
"jre_path": f"extension/jre/{DEFAULT_JRE_VERSION}-win32-x86_64/bin/java.exe",
"jdtls_launcher_jar_path": f"extension/server/plugins/org.eclipse.equinox.launcher_{eclipse_launcher_version}.jar",
"jdtls_readonly_config_path": "extension/server/config_win",
},
},
Expand All @@ -241,7 +243,7 @@ def _setup_runtime_dependencies(
"alternate_url": f"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioExptTeam/vsextensions/vscodeintellicode/{intellicode_version}/vspackage",
"archiveType": "zip",
"relative_extraction_path": "intellicode",
"sha256": INTELLICODE_SHA256 if default_intellicode_version else None,
"sha256": INTELLICODE_SHA256 if is_default_intellicode_version else None,
"allowed_hosts": INTELLICODE_ALLOWED_HOSTS,
"intellicode_jar_path": "extension/dist/com.microsoft.jdtls.intellicode.core-0.7.0.jar",
"intellisense_members_path": "extension/dist/bundledModels/java_intellisense-members",
Expand Down Expand Up @@ -273,17 +275,17 @@ def _setup_runtime_dependencies(
os.makedirs(vscode_java_path, exist_ok=True)
jre_home_path = str(PurePath(vscode_java_path, cast(str, dependency["jre_home_path"])))
jre_path = str(PurePath(vscode_java_path, cast(str, dependency["jre_path"])))
lombok_jar_path = str(PurePath(vscode_java_path, cast(str, dependency["lombok_jar_path"])))
jdtls_launcher_jar_path = str(PurePath(vscode_java_path, cast(str, dependency["jdtls_launcher_jar_path"])))
jdtls_readonly_config_path = str(PurePath(vscode_java_path, cast(str, dependency["jdtls_readonly_config_path"])))
lombok_dir = str(PurePath(vscode_java_path, "extension", "lombok"))
if not all(
[
os.path.exists(vscode_java_path),
os.path.exists(jre_home_path),
os.path.exists(jre_path),
os.path.exists(lombok_jar_path),
os.path.exists(jdtls_launcher_jar_path),
os.path.exists(jdtls_readonly_config_path),
bool(glob.glob(os.path.join(lombok_dir, "lombok-*.jar"))),
]
):
FileUtils.download_and_extract_archive_verified(
Expand All @@ -296,10 +298,14 @@ def _setup_runtime_dependencies(

os.chmod(jre_path, 0o755)

lombok_jars = glob.glob(os.path.join(lombok_dir, "lombok-*.jar"))
if len(lombok_jars) != 1:
raise RuntimeError(f"Expected exactly one lombok jar in {lombok_dir}, found: {lombok_jars}")
lombok_jar_path = lombok_jars[0]

assert os.path.exists(vscode_java_path)
assert os.path.exists(jre_home_path)
assert os.path.exists(jre_path)
assert os.path.exists(lombok_jar_path)
assert os.path.exists(jdtls_launcher_jar_path)
assert os.path.exists(jdtls_readonly_config_path)

Expand Down Expand Up @@ -698,7 +704,7 @@ def _get_initialize_params(self, repository_absolute_path: str) -> InitializePar
},
"workspaceCacheLimit": 90,
"runtimes": [
{"name": "JavaSE-21", "path": "static/vscode-java/extension/jre/21.0.7-linux-x86_64", "default": True}
{"name": "JavaSE-21", "path": "static/vscode-java/extension/jre/21.0.10-linux-x86_64", "default": True}
],
},
"trace": {"server": "verbose"},
Expand Down
4 changes: 4 additions & 0 deletions src/solidlsp/ls_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os
import platform
import shutil
import stat
import subprocess
import tarfile
import uuid
Expand Down Expand Up @@ -387,6 +388,9 @@ def _extract_zip_archive(archive_path: str, target_path: str) -> None:
continue

os.makedirs(os.path.dirname(extracted_path), exist_ok=True)
if os.path.exists(extracted_path):
# Some archives are re-extracted over an older read-only install.
os.chmod(extracted_path, os.stat(extracted_path).st_mode | stat.S_IWUSR)
with zip_ref.open(zip_info, "r") as source_file, open(extracted_path, "wb") as output_file:
shutil.copyfileobj(source_file, output_file)

Expand Down
19 changes: 19 additions & 0 deletions test/solidlsp/util/test_ls_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import hashlib
import stat
import zipfile
from pathlib import Path
from unittest.mock import patch

Expand Down Expand Up @@ -40,3 +42,20 @@ def test_download_file_verified_writes_decoded_response_body(tmp_path: Path) ->
)

assert target_path.read_bytes() == payload


def test_extract_zip_archive_overwrites_existing_read_only_file(tmp_path: Path) -> None:
"""Existing read-only files should be made writable before extraction overwrites them."""
archive_path = tmp_path / "archive.zip"
extracted_file = tmp_path / "extract" / "nested" / "file.txt"

with zipfile.ZipFile(archive_path, "w") as zip_file:
zip_file.writestr("nested/file.txt", "new content")

extracted_file.parent.mkdir(parents=True)
extracted_file.write_text("old content")
extracted_file.chmod(extracted_file.stat().st_mode & ~stat.S_IWUSR)

FileUtils._extract_zip_archive(str(archive_path), str(tmp_path / "extract"))

assert extracted_file.read_text() == "new content"
Loading