feat(jdtls): surface Lombok-generated methods in document symbols, bump jdtls version (#1434)

* feat(jdtls): surface Lombok-generated methods in document symbols

JDTLS' DocumentSymbolHandler filtered out every SourceMethod where
JDTUtils.isGenerated() was true, so Lombok-synthesised methods (getX/setX,
builder(), equals/hashCode/toString, withX, fluent accessors, ...) never
reached the LSP client. As a result Serena's symbol-aware tools — find_symbol,
get_symbols_overview, replace_symbol_body, insert_before_symbol,
insert_after_symbol — could not see, address or edit any Lombok-generated
member; only search_for_pattern / replace_content (text-based) worked.

Upstream introduced an opt-in preference java.symbols.includeGeneratedCode
in eclipse-jdtls/eclipse.jdt.ls#3706 (merged 2026-02-24) and surfaced it
through redhat-developer/vscode-java#4335. First vscode-java release with
the new JDTLS build is v1.53.0 (2026-02-26). Serena was pinned to v1.42.0-561,
well before the fix existed.

Changes:
- New ls_specific_settings.java.lombok_show_generated setting (default true);
  forwarded to JDTLS as java.symbols.includeGeneratedCode in initialize
  params. Older servers ignore the unknown preference key silently.
- Bump bundled vscode-java DEFAULT 1.42.0-561 -> 1.54.0-923 (brings JRE
  21.0.10, Lombok 1.18.39 and Equinox launcher 1.7.100). INITIAL stays at
  1.42.0-561, so per the per-version install-dir convention from #1438 the
  new build lands in vscode-java-1.54.0-923/ while legacy vscode-java/
  caches and any explicit vscode_java_version: '1.42.0-561' override keep
  working.
- New VsixResourcePaths dataclass + INITIAL/DEFAULT_VSCODE_JAVA_PATHS pin
  the JRE / Lombok / launcher filenames *inside* the VSIX. Without this,
  bumping the archive would break users on legacy 1.42.0-561 because
  resource paths inside the archive change between releases. Resolution
  mirrors SHA pinning: known versions get their pinned paths; any other
  version raises SolidLSPException with a pointer to upstream-jdtls mode
  (which derives paths dynamically and needs no pinning).
- Drop dead 'static/vscode-java/...' placeholder in initialize params; the
  real runtimes[] is overwritten below from runtime_dependency_paths
  .jre_home_path, which is correct in both default-VSIX and upstream-jdtls
  modes.
- Test fixtures: Lombok provided dep in test_repo/pom.xml plus
  LombokModel / FluentLombokModel / DefaultGreeter covering @Data, @Builder
  (toBuilder=true), @With, @Delegate, @AllArgsConstructor, @NoArgsConstructor
  and @Accessors(fluent=true). Regression test in test_java_basic.py
  asserts every generated-method shape lands in document symbols.

Closes #1432
This commit is contained in:
a-simeshin authored and GitHub committed 2026-05-05 15:06:04 +02:00
1 parent 3122384822
commit ba6170bab1
8 files changed
+229 -35

No files matched your search

+52
View File
@@ -96,3 +96,55 @@ class TestJavaLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True)
def test_lombok_generated_methods_visible_by_default(self, language_server: SolidLanguageServer) -> None:
"""Generated Lombok methods must appear in document symbols across the common annotations.
Default `lombok_show_generated=True` sends `java.symbols.includeGeneratedCode=true` to JDTLS,
which disables the SourceMethod-isGenerated filter in DocumentSymbolHandler. Without it,
find_symbol/get_symbols_overview cannot reach Lombok-synthesised methods at all (#1432).
Covers @Data, @Builder(toBuilder=true), @With, @AllArgsConstructor, @NoArgsConstructor,
@Delegate and @Accessors(fluent=true) — every method-generating annotation listed in the
issue plus fluent prefix-stripped accessors and @Delegate forwarders.
"""
def _names_by_kind(doc, kind: int) -> set[str]:
return {sym.get("name") for sym in doc.get_all_symbols_and_roots()[0] if sym.get("kind") == kind}
SYMBOL_KIND_CLASS = 5
SYMBOL_KIND_METHOD = 6
SYMBOL_KIND_CONSTRUCTOR = 9
# ---- LombokModel: @Data + @Builder(toBuilder=true) + @With + ctors + @Delegate -------
lombok_path = os.path.join("src", "main", "java", "test_repo", "LombokModel.java")
lombok_doc = language_server.request_document_symbols(lombok_path)
lombok_methods = _names_by_kind(lombok_doc, SYMBOL_KIND_METHOD)
# @Data getters/setters (prefixed) + canonical Object overrides
for expected in ("getName", "getAge", "setName", "setAge", "equals", "hashCode", "toString"):
assert expected in lombok_methods, f"@Data did not surface {expected!r}; got: {sorted(lombok_methods)}"
# @Builder(toBuilder=true): static factory + instance toBuilder + inner build()
for expected in ("builder", "toBuilder", "build"):
assert expected in lombok_methods, f"@Builder did not surface {expected!r}; got: {sorted(lombok_methods)}"
# @With: copy-with methods
for expected in ("withName", "withAge"):
assert expected in lombok_methods, f"@With did not surface {expected!r}; got: {sorted(lombok_methods)}"
# @Delegate: forwarder methods for every method of the delegate target
for expected in ("greet", "farewell"):
assert expected in lombok_methods, f"@Delegate did not surface forwarder {expected!r}; got: {sorted(lombok_methods)}"
# @Builder generates an inner builder class
lombok_classes = _names_by_kind(lombok_doc, SYMBOL_KIND_CLASS)
assert "LombokModelBuilder" in lombok_classes, f"@Builder inner class missing; got: {sorted(lombok_classes)}"
# @AllArgsConstructor + @NoArgsConstructor surface as ctor symbols (kind=9)
lombok_ctors = _names_by_kind(lombok_doc, SYMBOL_KIND_CONSTRUCTOR)
assert "LombokModel" in lombok_ctors, f"Lombok ctors missing; got ctors {sorted(lombok_ctors)}"
# ---- FluentLombokModel: @Accessors(fluent=true) - prefix-stripped accessors ---------
fluent_path = os.path.join("src", "main", "java", "test_repo", "FluentLombokModel.java")
fluent_doc = language_server.request_document_symbols(fluent_path)
fluent_methods = _names_by_kind(fluent_doc, SYMBOL_KIND_METHOD)
for expected in ("host", "tag"):
assert expected in fluent_methods, f"@Accessors(fluent=true) did not surface {expected!r}; got: {sorted(fluent_methods)}"