java-functional-lsp 0.9.14__tar.gz → 0.9.16__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.9.16/.claude-plugin/plugin.json +16 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/PKG-INFO +21 -7
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/README.md +20 -6
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/SKILL.md +3 -6
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/intellij/README.md +1 -1
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/pyproject.toml +1 -1
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/cli.py +34 -25
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_cli.py +27 -1
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_server.py +37 -12
- java_functional_lsp-0.9.14/.claude-plugin/plugin.json +0 -5
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.gitignore +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/LICENSE +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/merkle.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/proxy.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/server.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/__init__.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/conftest.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_base.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_config.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_merkle.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_merkle_proxy.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/tests/test_suppress.py +0 -0
- {java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/uv.lock +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "java-functional-lsp",
|
|
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.2",
|
|
5
|
+
"lspServers": {
|
|
6
|
+
"java-functional": {
|
|
7
|
+
"command": "java-functional-lsp",
|
|
8
|
+
"extensionToLanguage": {
|
|
9
|
+
".java": "java"
|
|
10
|
+
},
|
|
11
|
+
"startupTimeout": 120000,
|
|
12
|
+
"restartOnCrash": true,
|
|
13
|
+
"maxRestarts": 5
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.16
|
|
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
|
|
@@ -136,11 +136,17 @@ See [editors/intellij/README.md](editors/intellij/README.md) for detailed instru
|
|
|
136
136
|
|
|
137
137
|
**Step 1: Enable LSP support** (required, one-time):
|
|
138
138
|
|
|
139
|
-
Add to `~/.claude/settings.json
|
|
139
|
+
Add `lspServers` to `~/.claude/settings.json` (the plugin handles this automatically — only needed for manual setup):
|
|
140
140
|
```json
|
|
141
141
|
{
|
|
142
|
-
"
|
|
143
|
-
"
|
|
142
|
+
"lspServers": {
|
|
143
|
+
"java-functional": {
|
|
144
|
+
"command": "java-functional-lsp",
|
|
145
|
+
"extensionToLanguage": { ".java": "java" },
|
|
146
|
+
"startupTimeout": 120000,
|
|
147
|
+
"restartOnCrash": true,
|
|
148
|
+
"maxRestarts": 5
|
|
149
|
+
}
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
152
|
```
|
|
@@ -160,19 +166,27 @@ Or manually add to your Claude Code config:
|
|
|
160
166
|
"lspServers": {
|
|
161
167
|
"java-functional": {
|
|
162
168
|
"command": "java-functional-lsp",
|
|
163
|
-
"extensionToLanguage": { ".java": "java" }
|
|
169
|
+
"extensionToLanguage": { ".java": "java" },
|
|
170
|
+
"startupTimeout": 120000,
|
|
171
|
+
"restartOnCrash": true,
|
|
172
|
+
"maxRestarts": 5
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
}
|
|
167
176
|
```
|
|
168
177
|
|
|
178
|
+
(`startupTimeout: 120000` accommodates jdtls cold-start; `restartOnCrash` keeps the server alive across session.)
|
|
179
|
+
|
|
169
180
|
**Alternative: project-level `.lsp.json`** — instead of installing the plugin or editing global config, add a `.lsp.json` file at your project root:
|
|
170
181
|
|
|
171
182
|
```json
|
|
172
183
|
{
|
|
173
184
|
"java-functional": {
|
|
174
185
|
"command": "java-functional-lsp",
|
|
175
|
-
"extensionToLanguage": { ".java": "java" }
|
|
186
|
+
"extensionToLanguage": { ".java": "java" },
|
|
187
|
+
"startupTimeout": 120000,
|
|
188
|
+
"restartOnCrash": true,
|
|
189
|
+
"maxRestarts": 5
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
192
|
```
|
|
@@ -198,7 +212,7 @@ moving on. Fix any type errors or missing imports immediately.
|
|
|
198
212
|
|
|
199
213
|
| Issue | Fix |
|
|
200
214
|
|-------|-----|
|
|
201
|
-
| No diagnostics appear | Ensure `
|
|
215
|
+
| No diagnostics appear | Ensure `lspServers` is configured (plugin or settings.json), restart |
|
|
202
216
|
| "java-functional-lsp not found" | Run `brew install aviadshiber/tap/java-functional-lsp` |
|
|
203
217
|
| Plugin not active | Run `claude plugin list` to verify, then `/reload-plugins` |
|
|
204
218
|
| Diagnostics slow on first open | Normal — tree-sitter parses on first load, then incremental |
|
|
@@ -108,11 +108,17 @@ See [editors/intellij/README.md](editors/intellij/README.md) for detailed instru
|
|
|
108
108
|
|
|
109
109
|
**Step 1: Enable LSP support** (required, one-time):
|
|
110
110
|
|
|
111
|
-
Add to `~/.claude/settings.json
|
|
111
|
+
Add `lspServers` to `~/.claude/settings.json` (the plugin handles this automatically — only needed for manual setup):
|
|
112
112
|
```json
|
|
113
113
|
{
|
|
114
|
-
"
|
|
115
|
-
"
|
|
114
|
+
"lspServers": {
|
|
115
|
+
"java-functional": {
|
|
116
|
+
"command": "java-functional-lsp",
|
|
117
|
+
"extensionToLanguage": { ".java": "java" },
|
|
118
|
+
"startupTimeout": 120000,
|
|
119
|
+
"restartOnCrash": true,
|
|
120
|
+
"maxRestarts": 5
|
|
121
|
+
}
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
```
|
|
@@ -132,19 +138,27 @@ Or manually add to your Claude Code config:
|
|
|
132
138
|
"lspServers": {
|
|
133
139
|
"java-functional": {
|
|
134
140
|
"command": "java-functional-lsp",
|
|
135
|
-
"extensionToLanguage": { ".java": "java" }
|
|
141
|
+
"extensionToLanguage": { ".java": "java" },
|
|
142
|
+
"startupTimeout": 120000,
|
|
143
|
+
"restartOnCrash": true,
|
|
144
|
+
"maxRestarts": 5
|
|
136
145
|
}
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
148
|
```
|
|
140
149
|
|
|
150
|
+
(`startupTimeout: 120000` accommodates jdtls cold-start; `restartOnCrash` keeps the server alive across session.)
|
|
151
|
+
|
|
141
152
|
**Alternative: project-level `.lsp.json`** — instead of installing the plugin or editing global config, add a `.lsp.json` file at your project root:
|
|
142
153
|
|
|
143
154
|
```json
|
|
144
155
|
{
|
|
145
156
|
"java-functional": {
|
|
146
157
|
"command": "java-functional-lsp",
|
|
147
|
-
"extensionToLanguage": { ".java": "java" }
|
|
158
|
+
"extensionToLanguage": { ".java": "java" },
|
|
159
|
+
"startupTimeout": 120000,
|
|
160
|
+
"restartOnCrash": true,
|
|
161
|
+
"maxRestarts": 5
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
```
|
|
@@ -170,7 +184,7 @@ moving on. Fix any type errors or missing imports immediately.
|
|
|
170
184
|
|
|
171
185
|
| Issue | Fix |
|
|
172
186
|
|-------|-----|
|
|
173
|
-
| No diagnostics appear | Ensure `
|
|
187
|
+
| No diagnostics appear | Ensure `lspServers` is configured (plugin or settings.json), restart |
|
|
174
188
|
| "java-functional-lsp not found" | Run `brew install aviadshiber/tap/java-functional-lsp` |
|
|
175
189
|
| Plugin not active | Run `claude plugin list` to verify, then `/reload-plugins` |
|
|
176
190
|
| Diagnostics slow on first open | Normal — tree-sitter parses on first load, then incremental |
|
|
@@ -112,14 +112,11 @@ To release a new version:
|
|
|
112
112
|
|
|
113
113
|
## Enabling LSP in Claude Code
|
|
114
114
|
|
|
115
|
-
LSP
|
|
116
|
-
```json
|
|
117
|
-
{ "env": { "ENABLE_LSP_TOOL": "1" } }
|
|
118
|
-
```
|
|
115
|
+
Declare `lspServers` in `~/.claude/settings.json` or in `plugin.json` — Claude Code enables the LSP tool automatically when `lspServers` is configured. `ENABLE_LSP_TOOL=1` is no longer needed.
|
|
119
116
|
|
|
120
117
|
For containers or CI, add a `.lsp.json` at the project root instead of installing the plugin:
|
|
121
118
|
```json
|
|
122
|
-
{ "java-functional": { "command": "java-functional-lsp", "extensionToLanguage": { ".java": "java" } } }
|
|
119
|
+
{ "java-functional": { "command": "java-functional-lsp", "extensionToLanguage": { ".java": "java" }, "startupTimeout": 120000, "restartOnCrash": true, "maxRestarts": 5 } }
|
|
123
120
|
```
|
|
124
121
|
|
|
125
122
|
To nudge Claude to act on diagnostics, add to your project's `CLAUDE.md`:
|
|
@@ -130,7 +127,7 @@ Fix any violations immediately — do not explain, just apply the fix.
|
|
|
130
127
|
|
|
131
128
|
## Troubleshooting
|
|
132
129
|
|
|
133
|
-
- **No diagnostics in Claude Code**: Ensure `
|
|
130
|
+
- **No diagnostics in Claude Code**: Ensure `lspServers` is configured (in `plugin.json` or `settings.json`), restart Claude Code
|
|
134
131
|
- **"java-functional-lsp not found"**: Run `brew install aviadshiber/tap/java-functional-lsp`
|
|
135
132
|
- **No completions/hover**: Install jdtls: `brew install jdtls` (requires JDK 21+)
|
|
136
133
|
- **Too many warnings**: Create `.java-functional-lsp.json` with `excludes` or per-rule severity
|
|
@@ -84,7 +84,7 @@ The server automatically detects JetBrains IDEs and disables the jdtls proxy —
|
|
|
84
84
|
### No diagnostics appear
|
|
85
85
|
- Ensure the file mapping is set to Language: `Java`, Language ID: `java`
|
|
86
86
|
- Check that `.java-functional-lsp.json` doesn't have rules set to `"off"`
|
|
87
|
-
- Try restarting the language server: **
|
|
87
|
+
- Try restarting the language server: **Language Servers** (search it in intellij Actions [Double Shift on keyboard]) → click on java-functional-lsp → **Restart**
|
|
88
88
|
- If diagnostics worked before but stopped, the jdtls proxy may be interfering. Set `JAVA_FUNCTIONAL_LSP_JDTLS=off` in the server command environment to force-disable jdtls. The server auto-detects JetBrains IDEs and disables jdtls by default (since IntelliJ provides native Java support), but this can be overridden with `JAVA_FUNCTIONAL_LSP_JDTLS=on`.
|
|
89
89
|
|
|
90
90
|
### PATH not found
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "java-functional-lsp"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.16"
|
|
8
8
|
description = "Java LSP server enforcing functional programming best practices — null safety, immutability, no exceptions"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -66,14 +66,41 @@ def format_diagnostic(path: Path, d: Diagnostic) -> str:
|
|
|
66
66
|
return f"{path}:{d.line + 1}:{d.col}: [{sym}] {d.code}: {d.message}"
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
_USAGE = """\
|
|
70
|
+
Usage: java-functional-lsp check <file.java> [file2.java ...]
|
|
71
|
+
java-functional-lsp check --dir <directory>
|
|
72
|
+
java-functional-lsp (start LSP server on stdio)"""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _collect_files(args: list[str]) -> list[Path]:
|
|
76
|
+
"""Resolve CLI args to a list of .java paths."""
|
|
77
|
+
files: list[Path] = []
|
|
78
|
+
if args and args[0] == "--dir":
|
|
79
|
+
if len(args) < 2:
|
|
80
|
+
print("Error: --dir requires a directory path", file=sys.stderr)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
directory = Path(args[1])
|
|
83
|
+
if not directory.is_dir():
|
|
84
|
+
print(f"Error: {directory} is not a directory", file=sys.stderr)
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
return sorted(directory.rglob("*.java"))
|
|
87
|
+
for arg in args:
|
|
88
|
+
p = Path(arg)
|
|
89
|
+
if p.is_file():
|
|
90
|
+
files.append(p)
|
|
91
|
+
elif p.is_dir():
|
|
92
|
+
files.extend(sorted(p.rglob("*.java")))
|
|
93
|
+
else:
|
|
94
|
+
print(f"Warning: {arg} not found, skipping", file=sys.stderr)
|
|
95
|
+
return files
|
|
96
|
+
|
|
97
|
+
|
|
69
98
|
def main() -> None:
|
|
70
99
|
"""CLI entry point: java-functional-lsp check <files...>"""
|
|
71
100
|
args = sys.argv[1:]
|
|
72
101
|
|
|
73
102
|
if args and args[0] in ("-h", "--help"):
|
|
74
|
-
print(
|
|
75
|
-
print(" java-functional-lsp check --dir <directory>")
|
|
76
|
-
print(" java-functional-lsp (start LSP server on stdio)")
|
|
103
|
+
print(_USAGE)
|
|
77
104
|
sys.exit(0)
|
|
78
105
|
|
|
79
106
|
if not args or args[0] != "check":
|
|
@@ -85,34 +112,16 @@ def main() -> None:
|
|
|
85
112
|
|
|
86
113
|
args = args[1:] # skip "check"
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if len(args) < 2:
|
|
92
|
-
print("Error: --dir requires a directory path", file=sys.stderr)
|
|
93
|
-
sys.exit(1)
|
|
94
|
-
directory = Path(args[1])
|
|
95
|
-
if not directory.is_dir():
|
|
96
|
-
print(f"Error: {directory} is not a directory", file=sys.stderr)
|
|
97
|
-
sys.exit(1)
|
|
98
|
-
files = sorted(directory.rglob("*.java"))
|
|
99
|
-
else:
|
|
100
|
-
for arg in args:
|
|
101
|
-
p = Path(arg)
|
|
102
|
-
if p.is_file():
|
|
103
|
-
files.append(p)
|
|
104
|
-
elif p.is_dir():
|
|
105
|
-
files.extend(sorted(p.rglob("*.java")))
|
|
106
|
-
else:
|
|
107
|
-
print(f"Warning: {arg} not found, skipping", file=sys.stderr)
|
|
115
|
+
if args and args[0] in ("-h", "--help"):
|
|
116
|
+
print(_USAGE)
|
|
117
|
+
sys.exit(0)
|
|
108
118
|
|
|
119
|
+
files = _collect_files(args)
|
|
109
120
|
if not files:
|
|
110
121
|
print("No .java files found", file=sys.stderr)
|
|
111
122
|
sys.exit(1)
|
|
112
123
|
|
|
113
|
-
# Load config from first file's directory
|
|
114
124
|
config = load_config(files[0])
|
|
115
|
-
|
|
116
125
|
total_diags = 0
|
|
117
126
|
excludes: list[str] = config.get("excludes", [])
|
|
118
127
|
for path in files:
|
|
@@ -4,8 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any
|
|
7
|
+
from unittest.mock import patch
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from java_functional_lsp.cli import check_file, format_diagnostic, load_config, main
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class TestCheckFile:
|
|
@@ -53,3 +56,26 @@ class TestLoadConfig:
|
|
|
53
56
|
def test_missing_config(self, tmp_path: Path) -> None:
|
|
54
57
|
config = load_config(tmp_path / "Test.java")
|
|
55
58
|
assert config == {}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TestHelp:
|
|
62
|
+
def test_top_level_help(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
63
|
+
with patch("sys.argv", ["java-functional-lsp", "--help"]):
|
|
64
|
+
with pytest.raises(SystemExit) as exc:
|
|
65
|
+
main()
|
|
66
|
+
assert exc.value.code == 0
|
|
67
|
+
assert "check" in capsys.readouterr().out
|
|
68
|
+
|
|
69
|
+
def test_check_subcommand_help(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
70
|
+
with patch("sys.argv", ["java-functional-lsp", "check", "--help"]):
|
|
71
|
+
with pytest.raises(SystemExit) as exc:
|
|
72
|
+
main()
|
|
73
|
+
assert exc.value.code == 0
|
|
74
|
+
assert "check" in capsys.readouterr().out
|
|
75
|
+
|
|
76
|
+
def test_check_short_help(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
77
|
+
with patch("sys.argv", ["java-functional-lsp", "check", "-h"]):
|
|
78
|
+
with pytest.raises(SystemExit) as exc:
|
|
79
|
+
main()
|
|
80
|
+
assert exc.value.code == 0
|
|
81
|
+
assert "check" in capsys.readouterr().out
|
|
@@ -71,10 +71,15 @@ async def lsp_client(tmp_path: Any) -> LanguageClient: # type: ignore[misc]
|
|
|
71
71
|
|
|
72
72
|
# Collect published diagnostics so tests can assert on them.
|
|
73
73
|
client._published: dict[str, list[lsp.Diagnostic]] = {} # type: ignore[attr-defined]
|
|
74
|
+
# Per-URI events signalled by on_publish — used by _open_and_wait_for_diagnostics.
|
|
75
|
+
client._diag_events: dict[str, asyncio.Event] = {} # type: ignore[attr-defined]
|
|
74
76
|
|
|
75
77
|
@client.feature(lsp.TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS)
|
|
76
78
|
def on_publish(params: lsp.PublishDiagnosticsParams) -> None:
|
|
77
79
|
client._published[params.uri] = list(params.diagnostics) # type: ignore[attr-defined]
|
|
80
|
+
evt = client._diag_events.get(params.uri) # type: ignore[attr-defined]
|
|
81
|
+
if evt is not None:
|
|
82
|
+
evt.set()
|
|
78
83
|
|
|
79
84
|
await client.start_io(sys.executable, "-m", "java_functional_lsp")
|
|
80
85
|
|
|
@@ -104,12 +109,17 @@ async def lsp_client(tmp_path: Any) -> LanguageClient: # type: ignore[misc]
|
|
|
104
109
|
try:
|
|
105
110
|
yield client
|
|
106
111
|
finally:
|
|
112
|
+
# Teardown with timeouts: the server subprocess may be mid-jdtls-startup
|
|
113
|
+
# on a slow CI runner, causing shutdown_async or stop to hang indefinitely.
|
|
107
114
|
try:
|
|
108
|
-
await client.shutdown_async(None)
|
|
115
|
+
await asyncio.wait_for(client.shutdown_async(None), timeout=5.0)
|
|
109
116
|
client.exit(None)
|
|
110
117
|
except Exception:
|
|
111
118
|
pass
|
|
112
|
-
|
|
119
|
+
try:
|
|
120
|
+
await asyncio.wait_for(client.stop(), timeout=5.0)
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
|
113
123
|
|
|
114
124
|
|
|
115
125
|
async def _open_and_wait_for_diagnostics(
|
|
@@ -121,10 +131,21 @@ async def _open_and_wait_for_diagnostics(
|
|
|
121
131
|
) -> list[lsp.Diagnostic]:
|
|
122
132
|
"""Open a document and wait until publishDiagnostics arrives for its URI.
|
|
123
133
|
|
|
124
|
-
The server publishes diagnostics asynchronously after didOpen. We
|
|
125
|
-
|
|
134
|
+
The server publishes diagnostics asynchronously after didOpen. We register
|
|
135
|
+
an asyncio.Event that on_publish signals, then await it with wait_for so the
|
|
136
|
+
full timeout is always respected — even when the event loop is transiently
|
|
137
|
+
busy (e.g. jdtls subprocess creation on a slow macOS runner).
|
|
138
|
+
|
|
139
|
+
Note: the server always emits publishDiagnostics for every opened file
|
|
140
|
+
(including clean ones with zero findings) synchronously inside on_did_open,
|
|
141
|
+
before jdtls is involved. The event is therefore guaranteed to fire on the
|
|
142
|
+
first — and correct — notification for each URI in this test setup.
|
|
126
143
|
"""
|
|
127
144
|
client._published.pop(uri, None) # type: ignore[attr-defined]
|
|
145
|
+
# Register event before sending didOpen — no await between these two lines,
|
|
146
|
+
# so on_publish cannot fire and miss the registration.
|
|
147
|
+
event = asyncio.Event()
|
|
148
|
+
client._diag_events[uri] = event # type: ignore[attr-defined]
|
|
128
149
|
|
|
129
150
|
client.text_document_did_open(
|
|
130
151
|
lsp.DidOpenTextDocumentParams(
|
|
@@ -137,14 +158,18 @@ async def _open_and_wait_for_diagnostics(
|
|
|
137
158
|
)
|
|
138
159
|
)
|
|
139
160
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
try:
|
|
162
|
+
await asyncio.wait_for(event.wait(), timeout=timeout)
|
|
163
|
+
except asyncio.TimeoutError:
|
|
164
|
+
# pytest.fail() raises Failed (a BaseException subclass);
|
|
165
|
+
# the finally block below still runs before the exception propagates.
|
|
166
|
+
pytest.fail(f"Timed out waiting for publishDiagnostics on {uri}")
|
|
167
|
+
finally:
|
|
168
|
+
client._diag_events.pop(uri, None) # type: ignore[attr-defined]
|
|
145
169
|
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
# on_publish stores to _published before setting the event, so the key
|
|
171
|
+
# is guaranteed to exist here — use direct access to surface any violation.
|
|
172
|
+
return client._published[uri] # type: ignore[attr-defined]
|
|
148
173
|
|
|
149
174
|
|
|
150
175
|
# --------------------------------------------------------------------------
|
|
@@ -2077,7 +2102,7 @@ class TestCacheClear:
|
|
|
2077
2102
|
# --------------------------------------------------------------------------
|
|
2078
2103
|
|
|
2079
2104
|
|
|
2080
|
-
@pytest.mark.timeout(
|
|
2105
|
+
@pytest.mark.timeout(60) # covers fixture setup (subprocess spawn + initialize handshake) on slow macOS runners
|
|
2081
2106
|
class TestLspLifecycle:
|
|
2082
2107
|
"""Full LSP lifecycle tests via real stdio transport — zero mocks."""
|
|
2083
2108
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/ISSUE_TEMPLATE/feature-request.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/.github/workflows/update-homebrew.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/editors/intellij/lsp4ij-template.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.14 → java_functional_lsp-0.9.16}/src/java_functional_lsp/analyzers/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|