[BUG] jdtls-lsp: workspace data dir keyed on cwd basename — all same-named packages/worktrees collide onto one cache, forcing constant re-imports
Bug Description
The jdtls-lsp plugin launches jdtls with no -data argument, so jdtls falls back to its default workspace-cache location — which is derived from only the basename of the working directory:
# jdtls.py (Homebrew jdtls 1.58.0)
cwd_name = os.path.basename(os.getcwd())
jdtls_data_path = os.path.join(cachedir, "jdtls-" + sha1(cwd_name.encode()).hexdigest())
Any two project directories that share a basename therefore map to the same jdtls data dir and fight over one cache. Each time you switch between them, jdtls re-imports the workspace from scratch (cold start, full re-index). This is common and painful in two setups:
- Git worktrees — multiple worktrees of the same package all named
src/MyService. - Monorepos / multi-root build systems where many leaf packages share conventional names.
In my case, 4 worktrees all containing src/Package collapse to one cache:
1cb969fa23a029d0f0581916fe3f256a07964b33 <- /…/original-package/
1cb969fa23a029d0f0581916fe3f256a07964b33 <- /…/ungate-documentation/
1cb969fa23a029d0f0581916fe3f256a07964b33 <- /…/hide-negative-values/
1cb969fa23a029d0f0581916fe3f256a07964b33 <- /…/nameprefix-conditional/
Environment
- macOS (Apple Silicon), Claude Code 2.1.170
jdtls-lsp@claude-plugins-official1.0.0- jdtls 1.58.0 (Homebrew), Java 21
Reproduction
- Create two directories with the same basename in different parents, each a valid Java project (e.g.
~/a/MyServiceand~/b/MyService). - Open Claude Code in the first; run any LSP op so jdtls imports it.
- Open Claude Code in the second; run an LSP op.
- Observe both share
~/Library/Caches/jdtls/jdtls-<sha1(basename)>, and the second import evicts/re-imports the first. Switching back re-imports again.
Suggested fix
Have the plugin pass an explicit -data keyed on the full project path, using the args field (already in the lspServers schema) and the ${CLAUDE_PROJECT_DIR} substitution:
"jdtls": {
"command": "jdtls",
"args": ["-data", "${HOME}/Library/Caches/jdtls/claude${CLAUDE_PROJECT_DIR}"],
"extensionToLanguage": { ".java": "java" },
"startupTimeout": 120000
}
This gives each distinct workspace its own cache, eliminating cross-worktree thrash, and is verified working — jdtls.py only defaults -data when absent, so a passed value wins. (A hashed path would avoid deep nesting if preferred.)
Related (distinct issues): #64536 (per-subagent jdtls instances → RAM), #37431 (null rootUri/workspaceFolder, closed not-planned).