java-functional-lsp 0.11.2__tar.gz → 0.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.claude-plugin/plugin.json +2 -5
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/publish.yml +2 -2
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/release-drafter.yml +1 -1
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/stale.yml +1 -1
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/test.yml +5 -5
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/update-homebrew.yml +3 -3
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/vscode-ext.yml +2 -2
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/PKG-INFO +41 -18
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/README.md +39 -16
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/SKILL.md +5 -3
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/commands/lint-java.md +1 -1
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/package-lock.json +190 -159
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/package.json +1 -1
- java_functional_lsp-0.13.0/hooks/post_tool_lint.py +184 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/pyproject.toml +4 -2
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/base.py +29 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/mutation_checker.py +46 -6
- java_functional_lsp-0.13.0/src/java_functional_lsp/freshness_marker.py +78 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/proxy.py +47 -6
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/server.py +424 -30
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/conftest.py +16 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_config.py +27 -1
- java_functional_lsp-0.13.0/tests/test_diagnostics_freshness.py +785 -0
- java_functional_lsp-0.13.0/tests/test_e2e_cross_file.py +170 -0
- java_functional_lsp-0.13.0/tests/test_e2e_hold.py +294 -0
- java_functional_lsp-0.13.0/tests/test_freshness_marker.py +243 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_functional_checker.py +16 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_mutation_checker.py +61 -0
- java_functional_lsp-0.13.0/tests/test_plugin_manifest.py +33 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_server.py +2 -2
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/uv.lock +2 -2
- java_functional_lsp-0.11.2/hooks/post_tool_lint.py +0 -89
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.gitignore +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/LICENSE +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/__init__.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/handler_wiring.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/negotiator.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/probe.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/registry.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/capabilities/static_builder.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/src/java_functional_lsp/merkle.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/__init__.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_base.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/__init__.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/test_handler_wiring.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/test_negotiator.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/test_probe.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/test_registry.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_capabilities/test_static_builder.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_hooks.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_merkle.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_merkle_proxy.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_suppress.py +0 -0
- {java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/tests/test_version.py +0 -0
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "java-functional-lsp",
|
|
3
3
|
"description": "Java LSP with functional programming rules enforcement — null safety, immutability, no exceptions, Spring best practices. Wraps jdtls for full Java language support.",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.4",
|
|
5
5
|
"lspServers": {
|
|
6
6
|
"java-functional": {
|
|
7
7
|
"command": "java-functional-lsp",
|
|
8
8
|
"extensionToLanguage": {
|
|
9
9
|
".java": "java"
|
|
10
|
-
}
|
|
11
|
-
"startupTimeout": 120000,
|
|
12
|
-
"restartOnCrash": true,
|
|
13
|
-
"maxRestarts": 5
|
|
10
|
+
}
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
13
|
}
|
|
@@ -13,9 +13,9 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
environment: pypi
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
17
|
- name: Set up Python
|
|
18
|
-
uses: actions/setup-python@
|
|
18
|
+
uses: actions/setup-python@v7
|
|
19
19
|
with:
|
|
20
20
|
python-version: "3.12"
|
|
21
21
|
- name: Install build dependencies
|
|
@@ -15,9 +15,9 @@ jobs:
|
|
|
15
15
|
os: [ubuntu-latest, macos-latest]
|
|
16
16
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
17
|
steps:
|
|
18
|
-
- uses: actions/checkout@
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
19
|
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
-
uses: actions/setup-python@
|
|
20
|
+
uses: actions/setup-python@v7
|
|
21
21
|
with:
|
|
22
22
|
python-version: ${{ matrix.python-version }}
|
|
23
23
|
- name: Install uv
|
|
@@ -48,13 +48,13 @@ jobs:
|
|
|
48
48
|
# AND jdtls request forwarding end-to-end. The unit matrix above
|
|
49
49
|
# provides fast Python-version-specific feedback without jdtls.
|
|
50
50
|
steps:
|
|
51
|
-
- uses: actions/checkout@
|
|
51
|
+
- uses: actions/checkout@v7
|
|
52
52
|
- name: Set up Python 3.12
|
|
53
|
-
uses: actions/setup-python@
|
|
53
|
+
uses: actions/setup-python@v7
|
|
54
54
|
with:
|
|
55
55
|
python-version: "3.12"
|
|
56
56
|
- name: Install Java 21
|
|
57
|
-
uses: actions/setup-java@
|
|
57
|
+
uses: actions/setup-java@v6
|
|
58
58
|
with:
|
|
59
59
|
distribution: temurin
|
|
60
60
|
java-version: "21"
|
{java_functional_lsp-0.11.2 → java_functional_lsp-0.13.0}/.github/workflows/update-homebrew.yml
RENAMED
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
15
|
- name: Get release version
|
|
16
16
|
id: version
|
|
17
17
|
env:
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
VERSION=$(gh api "repos/${{ github.repository }}/releases/latest" --jq .tag_name)
|
|
21
21
|
echo "version=${VERSION#v}" >> "$GITHUB_OUTPUT"
|
|
22
22
|
- name: Set up Python
|
|
23
|
-
uses: actions/setup-python@
|
|
23
|
+
uses: actions/setup-python@v7
|
|
24
24
|
with:
|
|
25
25
|
python-version: "3.12"
|
|
26
26
|
- name: Wait for PyPI availability
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
echo "Generated formula:"
|
|
44
44
|
cat java-functional-lsp.rb
|
|
45
45
|
- name: Checkout tap repo
|
|
46
|
-
uses: actions/checkout@
|
|
46
|
+
uses: actions/checkout@v7
|
|
47
47
|
with:
|
|
48
48
|
repository: aviadshiber/homebrew-tap
|
|
49
49
|
token: ${{ secrets.TAP_GITHUB_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Java LSP server enforcing functional programming best practices — null safety, immutability, no exceptions
|
|
5
5
|
Project-URL: Homepage, https://github.com/aviadshiber/java-functional-lsp
|
|
6
6
|
Project-URL: Repository, https://github.com/aviadshiber/java-functional-lsp
|
|
@@ -66,7 +66,7 @@ Install jdtls separately: `brew install jdtls` (requires JDK 21+). The server au
|
|
|
66
66
|
| `catch-rethrow` | catch block that wraps + rethrows | `Try.of().toEither()` | — |
|
|
67
67
|
| `mutable-variable` | Local variable reassignment | Final variables + functional transforms | — |
|
|
68
68
|
| `imperative-loop` | `for`/`while` loops | `.map()`/`.filter()`/`.flatMap()`/`.foldLeft()` | — |
|
|
69
|
-
| `mutable-dto` | `@Data` or `@Setter` on class | `@Value` (immutable) | ✅ |
|
|
69
|
+
| `mutable-dto` | `@Data` or `@Setter` on class | `@Value` (immutable); `record` instead at `sourceLevel` 16+ | ✅ |
|
|
70
70
|
| `imperative-option-unwrap` | `if (opt.isDefined()) { opt.get() }` | `map()`/`flatMap()`/`fold()` | ✅ |
|
|
71
71
|
| `field-injection` | `@Autowired` on field | Constructor injection | — |
|
|
72
72
|
| `component-annotation` | `@Component`/`@Service`/`@Repository` | `@Configuration` + `@Bean` | — |
|
|
@@ -143,10 +143,7 @@ Add `lspServers` to `~/.claude/settings.json` (the plugin handles this automatic
|
|
|
143
143
|
"lspServers": {
|
|
144
144
|
"java-functional": {
|
|
145
145
|
"command": "java-functional-lsp",
|
|
146
|
-
"extensionToLanguage": { ".java": "java" }
|
|
147
|
-
"startupTimeout": 120000,
|
|
148
|
-
"restartOnCrash": true,
|
|
149
|
-
"maxRestarts": 5
|
|
146
|
+
"extensionToLanguage": { ".java": "java" }
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
149
|
}
|
|
@@ -190,27 +187,19 @@ Or manually add to your Claude Code config:
|
|
|
190
187
|
"lspServers": {
|
|
191
188
|
"java-functional": {
|
|
192
189
|
"command": "java-functional-lsp",
|
|
193
|
-
"extensionToLanguage": { ".java": "java" }
|
|
194
|
-
"startupTimeout": 120000,
|
|
195
|
-
"restartOnCrash": true,
|
|
196
|
-
"maxRestarts": 5
|
|
190
|
+
"extensionToLanguage": { ".java": "java" }
|
|
197
191
|
}
|
|
198
192
|
}
|
|
199
193
|
}
|
|
200
194
|
```
|
|
201
195
|
|
|
202
|
-
(`startupTimeout: 120000` accommodates jdtls cold-start; `restartOnCrash` keeps the server alive across session.)
|
|
203
|
-
|
|
204
196
|
**Alternative: project-level `.lsp.json`** — instead of installing the plugin or editing global config, add a `.lsp.json` file at your project root:
|
|
205
197
|
|
|
206
198
|
```json
|
|
207
199
|
{
|
|
208
200
|
"java-functional": {
|
|
209
201
|
"command": "java-functional-lsp",
|
|
210
|
-
"extensionToLanguage": { ".java": "java" }
|
|
211
|
-
"startupTimeout": 120000,
|
|
212
|
-
"restartOnCrash": true,
|
|
213
|
-
"maxRestarts": 5
|
|
202
|
+
"extensionToLanguage": { ".java": "java" }
|
|
214
203
|
}
|
|
215
204
|
}
|
|
216
205
|
```
|
|
@@ -240,6 +229,7 @@ moving on. Fix any type errors or missing imports immediately.
|
|
|
240
229
|
| "java-functional-lsp not found" | Run `brew install aviadshiber/tap/java-functional-lsp` |
|
|
241
230
|
| Plugin not active | Run `claude plugin list` to verify, then `/reload-plugins` |
|
|
242
231
|
| Diagnostics slow on first open | Normal — tree-sitter parses on first load, then incremental |
|
|
232
|
+
| Java errors show up one tool call after the edit | Claude Code doesn't wait for LSP diagnostics after Edit/Write ([anthropics/claude-code#93321](https://github.com/anthropics/claude-code/issues/93321)). The plugin's hook waits up to 3s for them; if jdtls is slower (large projects), raise `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` (max 4). See [Fresh jdtls diagnostics after edits](#fresh-jdtls-diagnostics-after-edits) |
|
|
243
233
|
|
|
244
234
|
### Other Editors
|
|
245
235
|
|
|
@@ -258,6 +248,7 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
258
248
|
```json
|
|
259
249
|
{
|
|
260
250
|
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
251
|
+
"sourceLevel": 17,
|
|
261
252
|
"rules": {
|
|
262
253
|
"null-literal-arg": "warning",
|
|
263
254
|
"throw-statement": "info",
|
|
@@ -270,12 +261,16 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
270
261
|
**Options:**
|
|
271
262
|
- `excludes` — glob patterns for files/directories to skip entirely (supports `**` for multi-segment wildcards)
|
|
272
263
|
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
264
|
+
- `sourceLevel` — the project's Java language/source level, as an int (e.g. `17`) or version string (`"17"`, legacy `"1.8"`). Also accepted as `javaVersion`. Defaults to `8` if unset, so existing configs are unaffected. Currently only changes the `mutable-dto` recommendation (see below); higher source levels unlock more rewrite targets over time (records, sealed types, pattern matching, text blocks).
|
|
273
265
|
- `suppressJdtlsPatterns` — list of regex patterns to suppress jdtls diagnostics (see below)
|
|
274
266
|
|
|
275
267
|
**Spring-aware behavior:**
|
|
276
268
|
- `throw-statement`, `catch-rethrow`, and `try-catch-to-monadic` are automatically suppressed inside `@Bean` methods
|
|
277
269
|
- `mutable-dto` suggests `@ConstructorBinding` instead of `@Value` when the class has `@ConfigurationProperties`
|
|
278
270
|
|
|
271
|
+
**Source-level-aware behavior:**
|
|
272
|
+
- `mutable-dto` recommends a `record` instead of `@Value` once `sourceLevel` is 16 or higher (records became final/non-preview in JDK 16) — a plain immutable DTO is simpler as a built-in `record` with no Lombok dependency. It still calls out `@Value` as the fallback when the class needs `@With`/`@Builder`/`@Jacksonized`, is used as a facade, or relies on AOP proxying (records are `final` and can't be proxied). Below `sourceLevel` 16 (including the default), the message and quick fix are unchanged — `@Value` only.
|
|
273
|
+
|
|
279
274
|
**Inline suppression** with `@SuppressWarnings`:
|
|
280
275
|
|
|
281
276
|
```java
|
|
@@ -340,6 +335,33 @@ The jdtls Eclipse workspace index is cached in `~/.cache/jdtls-data/`. Warm star
|
|
|
340
335
|
|
|
341
336
|
To force a clean rebuild: `rm -rf ~/.cache/jdtls-data/`
|
|
342
337
|
|
|
338
|
+
### Fresh jdtls diagnostics after edits
|
|
339
|
+
|
|
340
|
+
jdtls re-validates a file 0.4–2.4s after the last edit to *any* file, and its results carry no document version. So after an edit, java-functional-lsp holds back jdtls diagnostics for that file until jdtls publishes again, rather than re-sending the previous edit's results as if they were current ([#109](https://github.com/aviadshiber/java-functional-lsp/issues/109)):
|
|
341
|
+
|
|
342
|
+
| Mode | Default for | After an edit |
|
|
343
|
+
|------|-------------|---------------|
|
|
344
|
+
| `custom-first` | Claude Code | Custom diagnostics publish at once, without jdtls errors; the full set follows when jdtls has re-validated |
|
|
345
|
+
| `hold-all` | Other editors | Nothing is published for the file until jdtls has re-validated (no flickering squiggles) |
|
|
346
|
+
| `off` | — | Previous behavior: custom diagnostics plus the last known jdtls diagnostics after 150ms |
|
|
347
|
+
|
|
348
|
+
With Claude Code two more things happen:
|
|
349
|
+
|
|
350
|
+
- **The plugin's PostToolUse hook waits for fresh results.** Claude Code attaches diagnostics as soon as its PostToolUse hooks finish, without waiting for the server ([anthropics/claude-code#93321](https://github.com/anthropics/claude-code/issues/93321)). The server records when it last published final diagnostics for each file, and `hooks/post_tool_lint.py` waits until that is newer than the edit (at most `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` seconds, default 3, max 4), so jdtls's result for the edit arrives in the same tool result. The markers live in a private per-user temp directory and contain only a timestamp.
|
|
351
|
+
- **jdtls gets a didSave after each edit.** Claude Code writes the file itself; when the edited buffer matches the file on disk, the server forwards a didSave. Without it, jdtls can re-check dependent files against the old version of the edited one (e.g. a caller keeps reporting a constructor's old arity) and never correct them.
|
|
352
|
+
|
|
353
|
+
If jdtls doesn't publish in time (3s after the last edit, stretched when jdtls is slow, 10s at most), the last known jdtls diagnostics are used. Files whose module is still being imported, and files matched by `java.diagnostic.filter`, are never held. While you edit the same file in quick succession, its diagnostics update only once the burst settles.
|
|
354
|
+
|
|
355
|
+
Environment variables:
|
|
356
|
+
|
|
357
|
+
| Variable | Values | Effect |
|
|
358
|
+
|----------|--------|--------|
|
|
359
|
+
| `JAVA_FUNCTIONAL_LSP_DIAG_HOLD` | `custom-first`, `hold-all`, `off` | Overrides the mode above; `off` restores the previous behavior |
|
|
360
|
+
| `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` | seconds, `0`–`4` (default `3`) | How long the Claude Code PostToolUse hook waits for fresh jdtls diagnostics; `0` disables the wait |
|
|
361
|
+
| `JAVA_FUNCTIONAL_LSP_LOG_LEVEL` | `DEBUG`, `INFO` (default), `WARNING` | Verbosity of java-functional-lsp's own logs; `DEBUG` adds one line per publish decision (file name, trigger, counts). Library logging (pygls) is unaffected — note that pygls already logs the JSON it sends, including diagnostic messages, at `INFO` |
|
|
362
|
+
|
|
363
|
+
The log reports `jdtls freshness: released=… too_early=… timeout=… late_correction=…` every 100 decisions and when jdtls stops. Repeated `timeout` lines mean jdtls is slow or stuck on that module. A growing `late_correction` count means jdtls results were released too early.
|
|
364
|
+
|
|
343
365
|
### Suppressing jdtls diagnostics
|
|
344
366
|
|
|
345
367
|
For project-specific jdtls false positives (e.g., annotation processor methods, MapStruct mappers), use `suppressJdtlsPatterns` to add custom regex filters:
|
|
@@ -366,7 +388,7 @@ The server provides LSP code actions (`textDocument/codeAction`) that automatica
|
|
|
366
388
|
| `null-return` | Replace with Option.none() | Rewrites `return null` → `return Option.none()`, adds import |
|
|
367
389
|
| `try-catch-to-monadic` | Convert try/catch to Try monadic flow | Rewrites `try { return expr; } catch (E e) { return default; }` → `Try.of(() -> expr).getOrElse(default)`. Supports 3 patterns: simple default (eager/lazy `.getOrElse`), logging + default (`.onFailure().getOrElse`), and exception-dependent recovery (`.recover(E.class, ...).get()`). Skips try-with-resources, finally, multi-catch, and union types. Adds import. |
|
|
368
390
|
| `imperative-option-unwrap` | Convert to Option.map().getOrElse() | Rewrites `if (opt.isDefined()) return opt.get(); else return X;` → `return opt.map(it -> ...).getOrElse(X);` (lazy `getOrElse(() -> ...)` for non-eager defaults). Bails on missing else or complex bodies. |
|
|
369
|
-
| `mutable-dto` | Replace @Data with @Value | Replaces the `@Data` annotation with `@Value` and adds `import lombok.Value`. Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations. |
|
|
391
|
+
| `mutable-dto` | Replace @Data with @Value | Replaces the `@Data` annotation with `@Value` and adds `import lombok.Value`. Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations. This quick fix is currently `@Value`-only regardless of `sourceLevel` — the diagnostic message/snippet recommend a `record` at `sourceLevel` 16+, but applying that rewrite safely (enumerating fields into record components) isn't automated yet; use the suggested snippet as a manual starting point. |
|
|
370
392
|
|
|
371
393
|
Quick fixes automatically add the required Vavr import if it's not already present. Disable auto-import with `"autoImportVavr": false` in config (`"autoImportLombok": false` for the Lombok import added by the `mutable-dto` fix).
|
|
372
394
|
|
|
@@ -404,6 +426,7 @@ This lets agents confidently apply fixes without guessing libraries or patterns
|
|
|
404
426
|
| `autoImportVavr` | `true` | Quick fixes auto-add Vavr/Option imports |
|
|
405
427
|
| `autoImportLombok` | `true` | The `mutable-dto` quick fix auto-adds `import lombok.Value` |
|
|
406
428
|
| `strictPurity` | `false` | When `true`, `impure-method` uses WARNING severity instead of HINT |
|
|
429
|
+
| `sourceLevel` (alias `javaVersion`) | `8` | Java source level; `mutable-dto` recommends `record` over `@Value` at 16+ |
|
|
407
430
|
|
|
408
431
|
> **Note:** The machine-readable `data` payload is always included in diagnostics when available — no configuration needed.
|
|
409
432
|
|
|
@@ -38,7 +38,7 @@ Install jdtls separately: `brew install jdtls` (requires JDK 21+). The server au
|
|
|
38
38
|
| `catch-rethrow` | catch block that wraps + rethrows | `Try.of().toEither()` | — |
|
|
39
39
|
| `mutable-variable` | Local variable reassignment | Final variables + functional transforms | — |
|
|
40
40
|
| `imperative-loop` | `for`/`while` loops | `.map()`/`.filter()`/`.flatMap()`/`.foldLeft()` | — |
|
|
41
|
-
| `mutable-dto` | `@Data` or `@Setter` on class | `@Value` (immutable) | ✅ |
|
|
41
|
+
| `mutable-dto` | `@Data` or `@Setter` on class | `@Value` (immutable); `record` instead at `sourceLevel` 16+ | ✅ |
|
|
42
42
|
| `imperative-option-unwrap` | `if (opt.isDefined()) { opt.get() }` | `map()`/`flatMap()`/`fold()` | ✅ |
|
|
43
43
|
| `field-injection` | `@Autowired` on field | Constructor injection | — |
|
|
44
44
|
| `component-annotation` | `@Component`/`@Service`/`@Repository` | `@Configuration` + `@Bean` | — |
|
|
@@ -115,10 +115,7 @@ Add `lspServers` to `~/.claude/settings.json` (the plugin handles this automatic
|
|
|
115
115
|
"lspServers": {
|
|
116
116
|
"java-functional": {
|
|
117
117
|
"command": "java-functional-lsp",
|
|
118
|
-
"extensionToLanguage": { ".java": "java" }
|
|
119
|
-
"startupTimeout": 120000,
|
|
120
|
-
"restartOnCrash": true,
|
|
121
|
-
"maxRestarts": 5
|
|
118
|
+
"extensionToLanguage": { ".java": "java" }
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
}
|
|
@@ -162,27 +159,19 @@ Or manually add to your Claude Code config:
|
|
|
162
159
|
"lspServers": {
|
|
163
160
|
"java-functional": {
|
|
164
161
|
"command": "java-functional-lsp",
|
|
165
|
-
"extensionToLanguage": { ".java": "java" }
|
|
166
|
-
"startupTimeout": 120000,
|
|
167
|
-
"restartOnCrash": true,
|
|
168
|
-
"maxRestarts": 5
|
|
162
|
+
"extensionToLanguage": { ".java": "java" }
|
|
169
163
|
}
|
|
170
164
|
}
|
|
171
165
|
}
|
|
172
166
|
```
|
|
173
167
|
|
|
174
|
-
(`startupTimeout: 120000` accommodates jdtls cold-start; `restartOnCrash` keeps the server alive across session.)
|
|
175
|
-
|
|
176
168
|
**Alternative: project-level `.lsp.json`** — instead of installing the plugin or editing global config, add a `.lsp.json` file at your project root:
|
|
177
169
|
|
|
178
170
|
```json
|
|
179
171
|
{
|
|
180
172
|
"java-functional": {
|
|
181
173
|
"command": "java-functional-lsp",
|
|
182
|
-
"extensionToLanguage": { ".java": "java" }
|
|
183
|
-
"startupTimeout": 120000,
|
|
184
|
-
"restartOnCrash": true,
|
|
185
|
-
"maxRestarts": 5
|
|
174
|
+
"extensionToLanguage": { ".java": "java" }
|
|
186
175
|
}
|
|
187
176
|
}
|
|
188
177
|
```
|
|
@@ -212,6 +201,7 @@ moving on. Fix any type errors or missing imports immediately.
|
|
|
212
201
|
| "java-functional-lsp not found" | Run `brew install aviadshiber/tap/java-functional-lsp` |
|
|
213
202
|
| Plugin not active | Run `claude plugin list` to verify, then `/reload-plugins` |
|
|
214
203
|
| Diagnostics slow on first open | Normal — tree-sitter parses on first load, then incremental |
|
|
204
|
+
| Java errors show up one tool call after the edit | Claude Code doesn't wait for LSP diagnostics after Edit/Write ([anthropics/claude-code#93321](https://github.com/anthropics/claude-code/issues/93321)). The plugin's hook waits up to 3s for them; if jdtls is slower (large projects), raise `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` (max 4). See [Fresh jdtls diagnostics after edits](#fresh-jdtls-diagnostics-after-edits) |
|
|
215
205
|
|
|
216
206
|
### Other Editors
|
|
217
207
|
|
|
@@ -230,6 +220,7 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
230
220
|
```json
|
|
231
221
|
{
|
|
232
222
|
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
223
|
+
"sourceLevel": 17,
|
|
233
224
|
"rules": {
|
|
234
225
|
"null-literal-arg": "warning",
|
|
235
226
|
"throw-statement": "info",
|
|
@@ -242,12 +233,16 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
242
233
|
**Options:**
|
|
243
234
|
- `excludes` — glob patterns for files/directories to skip entirely (supports `**` for multi-segment wildcards)
|
|
244
235
|
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
236
|
+
- `sourceLevel` — the project's Java language/source level, as an int (e.g. `17`) or version string (`"17"`, legacy `"1.8"`). Also accepted as `javaVersion`. Defaults to `8` if unset, so existing configs are unaffected. Currently only changes the `mutable-dto` recommendation (see below); higher source levels unlock more rewrite targets over time (records, sealed types, pattern matching, text blocks).
|
|
245
237
|
- `suppressJdtlsPatterns` — list of regex patterns to suppress jdtls diagnostics (see below)
|
|
246
238
|
|
|
247
239
|
**Spring-aware behavior:**
|
|
248
240
|
- `throw-statement`, `catch-rethrow`, and `try-catch-to-monadic` are automatically suppressed inside `@Bean` methods
|
|
249
241
|
- `mutable-dto` suggests `@ConstructorBinding` instead of `@Value` when the class has `@ConfigurationProperties`
|
|
250
242
|
|
|
243
|
+
**Source-level-aware behavior:**
|
|
244
|
+
- `mutable-dto` recommends a `record` instead of `@Value` once `sourceLevel` is 16 or higher (records became final/non-preview in JDK 16) — a plain immutable DTO is simpler as a built-in `record` with no Lombok dependency. It still calls out `@Value` as the fallback when the class needs `@With`/`@Builder`/`@Jacksonized`, is used as a facade, or relies on AOP proxying (records are `final` and can't be proxied). Below `sourceLevel` 16 (including the default), the message and quick fix are unchanged — `@Value` only.
|
|
245
|
+
|
|
251
246
|
**Inline suppression** with `@SuppressWarnings`:
|
|
252
247
|
|
|
253
248
|
```java
|
|
@@ -312,6 +307,33 @@ The jdtls Eclipse workspace index is cached in `~/.cache/jdtls-data/`. Warm star
|
|
|
312
307
|
|
|
313
308
|
To force a clean rebuild: `rm -rf ~/.cache/jdtls-data/`
|
|
314
309
|
|
|
310
|
+
### Fresh jdtls diagnostics after edits
|
|
311
|
+
|
|
312
|
+
jdtls re-validates a file 0.4–2.4s after the last edit to *any* file, and its results carry no document version. So after an edit, java-functional-lsp holds back jdtls diagnostics for that file until jdtls publishes again, rather than re-sending the previous edit's results as if they were current ([#109](https://github.com/aviadshiber/java-functional-lsp/issues/109)):
|
|
313
|
+
|
|
314
|
+
| Mode | Default for | After an edit |
|
|
315
|
+
|------|-------------|---------------|
|
|
316
|
+
| `custom-first` | Claude Code | Custom diagnostics publish at once, without jdtls errors; the full set follows when jdtls has re-validated |
|
|
317
|
+
| `hold-all` | Other editors | Nothing is published for the file until jdtls has re-validated (no flickering squiggles) |
|
|
318
|
+
| `off` | — | Previous behavior: custom diagnostics plus the last known jdtls diagnostics after 150ms |
|
|
319
|
+
|
|
320
|
+
With Claude Code two more things happen:
|
|
321
|
+
|
|
322
|
+
- **The plugin's PostToolUse hook waits for fresh results.** Claude Code attaches diagnostics as soon as its PostToolUse hooks finish, without waiting for the server ([anthropics/claude-code#93321](https://github.com/anthropics/claude-code/issues/93321)). The server records when it last published final diagnostics for each file, and `hooks/post_tool_lint.py` waits until that is newer than the edit (at most `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` seconds, default 3, max 4), so jdtls's result for the edit arrives in the same tool result. The markers live in a private per-user temp directory and contain only a timestamp.
|
|
323
|
+
- **jdtls gets a didSave after each edit.** Claude Code writes the file itself; when the edited buffer matches the file on disk, the server forwards a didSave. Without it, jdtls can re-check dependent files against the old version of the edited one (e.g. a caller keeps reporting a constructor's old arity) and never correct them.
|
|
324
|
+
|
|
325
|
+
If jdtls doesn't publish in time (3s after the last edit, stretched when jdtls is slow, 10s at most), the last known jdtls diagnostics are used. Files whose module is still being imported, and files matched by `java.diagnostic.filter`, are never held. While you edit the same file in quick succession, its diagnostics update only once the burst settles.
|
|
326
|
+
|
|
327
|
+
Environment variables:
|
|
328
|
+
|
|
329
|
+
| Variable | Values | Effect |
|
|
330
|
+
|----------|--------|--------|
|
|
331
|
+
| `JAVA_FUNCTIONAL_LSP_DIAG_HOLD` | `custom-first`, `hold-all`, `off` | Overrides the mode above; `off` restores the previous behavior |
|
|
332
|
+
| `JAVA_FUNCTIONAL_LSP_HOOK_WAIT` | seconds, `0`–`4` (default `3`) | How long the Claude Code PostToolUse hook waits for fresh jdtls diagnostics; `0` disables the wait |
|
|
333
|
+
| `JAVA_FUNCTIONAL_LSP_LOG_LEVEL` | `DEBUG`, `INFO` (default), `WARNING` | Verbosity of java-functional-lsp's own logs; `DEBUG` adds one line per publish decision (file name, trigger, counts). Library logging (pygls) is unaffected — note that pygls already logs the JSON it sends, including diagnostic messages, at `INFO` |
|
|
334
|
+
|
|
335
|
+
The log reports `jdtls freshness: released=… too_early=… timeout=… late_correction=…` every 100 decisions and when jdtls stops. Repeated `timeout` lines mean jdtls is slow or stuck on that module. A growing `late_correction` count means jdtls results were released too early.
|
|
336
|
+
|
|
315
337
|
### Suppressing jdtls diagnostics
|
|
316
338
|
|
|
317
339
|
For project-specific jdtls false positives (e.g., annotation processor methods, MapStruct mappers), use `suppressJdtlsPatterns` to add custom regex filters:
|
|
@@ -338,7 +360,7 @@ The server provides LSP code actions (`textDocument/codeAction`) that automatica
|
|
|
338
360
|
| `null-return` | Replace with Option.none() | Rewrites `return null` → `return Option.none()`, adds import |
|
|
339
361
|
| `try-catch-to-monadic` | Convert try/catch to Try monadic flow | Rewrites `try { return expr; } catch (E e) { return default; }` → `Try.of(() -> expr).getOrElse(default)`. Supports 3 patterns: simple default (eager/lazy `.getOrElse`), logging + default (`.onFailure().getOrElse`), and exception-dependent recovery (`.recover(E.class, ...).get()`). Skips try-with-resources, finally, multi-catch, and union types. Adds import. |
|
|
340
362
|
| `imperative-option-unwrap` | Convert to Option.map().getOrElse() | Rewrites `if (opt.isDefined()) return opt.get(); else return X;` → `return opt.map(it -> ...).getOrElse(X);` (lazy `getOrElse(() -> ...)` for non-eager defaults). Bails on missing else or complex bodies. |
|
|
341
|
-
| `mutable-dto` | Replace @Data with @Value | Replaces the `@Data` annotation with `@Value` and adds `import lombok.Value`. Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations. |
|
|
363
|
+
| `mutable-dto` | Replace @Data with @Value | Replaces the `@Data` annotation with `@Value` and adds `import lombok.Value`. Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations. This quick fix is currently `@Value`-only regardless of `sourceLevel` — the diagnostic message/snippet recommend a `record` at `sourceLevel` 16+, but applying that rewrite safely (enumerating fields into record components) isn't automated yet; use the suggested snippet as a manual starting point. |
|
|
342
364
|
|
|
343
365
|
Quick fixes automatically add the required Vavr import if it's not already present. Disable auto-import with `"autoImportVavr": false` in config (`"autoImportLombok": false` for the Lombok import added by the `mutable-dto` fix).
|
|
344
366
|
|
|
@@ -376,6 +398,7 @@ This lets agents confidently apply fixes without guessing libraries or patterns
|
|
|
376
398
|
| `autoImportVavr` | `true` | Quick fixes auto-add Vavr/Option imports |
|
|
377
399
|
| `autoImportLombok` | `true` | The `mutable-dto` quick fix auto-adds `import lombok.Value` |
|
|
378
400
|
| `strictPurity` | `false` | When `true`, `impure-method` uses WARNING severity instead of HINT |
|
|
401
|
+
| `sourceLevel` (alias `javaVersion`) | `8` | Java source level; `mutable-dto` recommends `record` over `@Value` at 16+ |
|
|
379
402
|
|
|
380
403
|
> **Note:** The machine-readable `data` payload is always included in diagnostics when available — no configuration needed.
|
|
381
404
|
|
|
@@ -34,7 +34,7 @@ Without jdtls, the server runs in standalone mode — custom rules still work, b
|
|
|
34
34
|
| `catch-rethrow` | catch wraps + rethrows | `Try.of().toEither()` | — |
|
|
35
35
|
| `mutable-variable` | Variable reassignment | Final + functional transforms | — |
|
|
36
36
|
| `imperative-loop` | `for`/`while` loops | `.map()`/`.filter()`/`.flatMap()` | — |
|
|
37
|
-
| `mutable-dto` | `@Data` or `@Setter` | `@Value` (immutable) | ✅ |
|
|
37
|
+
| `mutable-dto` | `@Data` or `@Setter` | `record` (source level 16+) or `@Value` (immutable) | ✅ |
|
|
38
38
|
| `imperative-option-unwrap` | `if (opt.isDefined()) { opt.get() }` | `map()`/`flatMap()`/`fold()` | ✅ |
|
|
39
39
|
| `field-injection` | `@Autowired` on field | Constructor injection | — |
|
|
40
40
|
| `component-annotation` | `@Component`/`@Service`/`@Repository` | `@Configuration` + `@Bean` | — |
|
|
@@ -53,7 +53,7 @@ Rules marked ✅ provide automated `textDocument/codeAction` fixes:
|
|
|
53
53
|
- **null-return** → "Replace with Option.none()" — replaces `null` with `Option.none()`, adds import
|
|
54
54
|
- **try-catch-to-monadic** → "Convert try/catch to Try monadic flow" — rewrites `try { return expr; } catch (E e) { return default; }` to `Try.of(() -> expr).getOrElse(default)`. Supports 3 patterns: simple default, logging + default (`.onFailure().getOrElse`), and exception-dependent recovery (`.recover(E.class, ...).get()`). Skips try-with-resources, finally, multi-catch, union types. Adds import.
|
|
55
55
|
- **imperative-option-unwrap** → "Convert to Option.map().getOrElse()" — rewrites `if (opt.isDefined()) return opt.get(); else return X;` to `return opt.map(it -> ...).getOrElse(X);` (lazy supplier for non-eager defaults). Bails on missing else or complex bodies.
|
|
56
|
-
- **mutable-dto** → "Replace @Data with @Value" — swaps the annotation and adds `import lombok.Value` (disable with `"autoImportLombok": false`). Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations.
|
|
56
|
+
- **mutable-dto** → "Replace @Data with @Value" — swaps the annotation and adds `import lombok.Value` (disable with `"autoImportLombok": false`). Skips `@Setter`, `@ConfigurationProperties`, and conflicting Lombok constructor annotations. At source level 16+ the *diagnostic* recommends a `record` for a plain immutable DTO (with `@Value` as the fallback for `@With`/`@Builder`/`@Jacksonized`/facade/AOP cases); the automated fix still applies `@Value`, so convert to a `record` manually where appropriate.
|
|
57
57
|
|
|
58
58
|
## Agent-Ready Diagnostics
|
|
59
59
|
|
|
@@ -77,6 +77,7 @@ Create `.java-functional-lsp.json` in your project root:
|
|
|
77
77
|
|
|
78
78
|
```json
|
|
79
79
|
{
|
|
80
|
+
"sourceLevel": 17,
|
|
80
81
|
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
81
82
|
"rules": {
|
|
82
83
|
"imperative-loop": "hint",
|
|
@@ -88,6 +89,7 @@ Create `.java-functional-lsp.json` in your project root:
|
|
|
88
89
|
}
|
|
89
90
|
```
|
|
90
91
|
|
|
92
|
+
- `sourceLevel` (alias `javaVersion`) — the project's Java source level, as an int (`17`) or string (`"17"`, legacy `"1.8"` → 8). Defaults to `8` if unset (existing configs unaffected). At `16+`, `mutable-dto` recommends a `record` over `@Value` for plain DTOs
|
|
91
93
|
- `excludes` — glob patterns to skip files/directories entirely
|
|
92
94
|
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
93
95
|
- `autoImportVavr` — quick fixes auto-add Vavr imports (default: `true`)
|
|
@@ -124,7 +126,7 @@ Declare `lspServers` in `~/.claude/settings.json` or in `plugin.json` — Claude
|
|
|
124
126
|
|
|
125
127
|
For containers or CI, add a `.lsp.json` at the project root instead of installing the plugin:
|
|
126
128
|
```json
|
|
127
|
-
{ "java-functional": { "command": "java-functional-lsp", "extensionToLanguage": { ".java": "java" }
|
|
129
|
+
{ "java-functional": { "command": "java-functional-lsp", "extensionToLanguage": { ".java": "java" } } }
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
To nudge Claude to act on diagnostics, add to your project's `CLAUDE.md`:
|
|
@@ -35,6 +35,6 @@ $ARGUMENTS - Java files, directories, or glob patterns to lint. If empty, lint a
|
|
|
35
35
|
- `throw-statement` → convert to `Either.left()` or `Try.of()`
|
|
36
36
|
- `mutable-variable` → make final, use functional transforms
|
|
37
37
|
- `imperative-loop` → replace with `.map()`, `.filter()`, `.flatMap()`
|
|
38
|
-
- `mutable-dto` → change `@Data` to `@Value`
|
|
38
|
+
- `mutable-dto` → convert to a `record` (source level 16+) or change `@Data` to `@Value` (follow the diagnostic's recommendation, which is source-level aware)
|
|
39
39
|
- `field-injection` → convert to constructor injection
|
|
40
40
|
- `component-annotation` → move to `@Configuration` class with `@Bean`
|