Skip to content

fix(lsp): resolve JDTLS root to topmost pom.xml in Java Maven multi-module projects#28761

Open
areyouok wants to merge 1 commit into
anomalyco:devfrom
areyouok:fix/jdtls-maven-multi-module
Open

fix(lsp): resolve JDTLS root to topmost pom.xml in Java Maven multi-module projects#28761
areyouok wants to merge 1 commit into
anomalyco:devfrom
areyouok:fix/jdtls-maven-multi-module

Conversation

@areyouok
Copy link
Copy Markdown

Issue for this PR

Fixes #28760

Type of change

  • Bug fix

What does this PR do?

The existing JDTLS root detection (introduced in #14192) correctly handles Gradle monorepos by walking up to settings.gradle, but for Maven projects it still returns the nearest pom.xml directory. This means each Maven submodule spawns its own JDTLS process.

This PR adds Maven multi-module awareness: the root function now walks up the pom.xml chain and checks whether each parent pom declares the child directory as a <module>. If the parent-child relationship is confirmed, it keeps going up until reaching the true topmost parent pom.

Key changes in packages/opencode/src/lsp/server.ts:

  • StrictNearestRoot helper: like NearestRoot but returns undefined when no marker is found (instead of defaulting to the instance directory). Used for Gradle/Eclipse paths to avoid false positives.
  • isModuleOf(pomContent, modulePath): parses <modules> blocks from pom.xml content, strips XML comments, normalizes paths (handles ./ prefix, trailing /), and checks if a directory is declared as a module.
  • Maven root resolution: uses Filesystem.findUp("pom.xml", ...) to collect all ancestor poms, then walks from innermost to outermost verifying <module> links. Stops when the chain breaks (parent does not declare child as module).

How did you verify your code works?

Added 16 unit tests in packages/opencode/test/lsp/jdtls-root.test.ts covering:

  • Single-module Maven project → returns pom.xml directory
  • Multi-module Maven project → walks up to top-level parent via <module> chain
  • Three-level Maven chain → resolves to top-level
  • Broken <module> chain → stops at the independent module
  • Nested independent project (not declared as module) → returns its own directory
  • <module> with ./ prefix and trailing / normalization
  • Multi-segment <module> paths (e.g. tools/sample)
  • XML-commented <module> declarations are ignored
  • Module name mismatch does not falsely match
  • Multiple <module> declarations handled correctly
  • Gradle projects (settings.gradle, build.gradle, gradlew) still work
  • Gradle monorepo takes precedence over nested pom.xml
  • Kotlin DSL variants (.kts) recognized
  • Maven and Gradle sibling projects do not interfere
  • No build markers → returns undefined

All tests pass, typecheck clean.

Screenshots / recordings

N/A (LSP behavior change, no UI impact)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Previously JDTLS.root() found the nearest pom.xml, causing each Maven
module to spawn its own jdtls instance. Now uses Filesystem.findUp with
rootFirst to find the topmost pom.xml, so all modules share one instance.

- Add StrictNearestRoot helper (returns undefined instead of ctx.directory)
- Preserve Gradle and Eclipse project detection behavior
- Add 12 unit tests for JDTLS root resolution
@areyouok areyouok changed the title fix(lsp): resolve JDTLS root to topmost pom.xml in Maven multi-module projects fix(lsp): resolve JDTLS root to topmost pom.xml in Java Maven multi-module projects May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JDTLS spawns separate process per module in Java Maven multi-module projects

1 participant