java-functional-lsp 0.8.0__tar.gz → 0.8.1__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.8.0 → java_functional_lsp-0.8.1}/PKG-INFO +3 -1
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/README.md +2 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/intellij/README.md +2 -1
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/pyproject.toml +1 -1
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/server.py +71 -3
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_server.py +94 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/uv.lock +4 -4
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.claude-plugin/plugin.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.gitignore +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/LICENSE +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/SKILL.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/src/java_functional_lsp/proxy.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/__init__.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/conftest.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_base.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_config.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/tests/test_suppress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -128,6 +128,8 @@ Use the [LSP4IJ](https://github.com/redhat-developer/lsp4ij) plugin (works on Co
|
|
|
128
128
|
2. **Settings** → **Languages & Frameworks** → **Language Servers** → **`+`**
|
|
129
129
|
3. Set **Command**: `java-functional-lsp`, then in **Mappings** → **File name patterns** add `*.java` with Language Id `java`
|
|
130
130
|
|
|
131
|
+
The server automatically detects JetBrains IDEs and disables the jdtls proxy (IntelliJ provides native Java support). To force-enable jdtls, set `JAVA_FUNCTIONAL_LSP_JDTLS=on` in the server command environment.
|
|
132
|
+
|
|
131
133
|
See [editors/intellij/README.md](editors/intellij/README.md) for detailed instructions.
|
|
132
134
|
|
|
133
135
|
### Claude Code
|
|
@@ -100,6 +100,8 @@ Use the [LSP4IJ](https://github.com/redhat-developer/lsp4ij) plugin (works on Co
|
|
|
100
100
|
2. **Settings** → **Languages & Frameworks** → **Language Servers** → **`+`**
|
|
101
101
|
3. Set **Command**: `java-functional-lsp`, then in **Mappings** → **File name patterns** add `*.java` with Language Id `java`
|
|
102
102
|
|
|
103
|
+
The server automatically detects JetBrains IDEs and disables the jdtls proxy (IntelliJ provides native Java support). To force-enable jdtls, set `JAVA_FUNCTIONAL_LSP_JDTLS=on` in the server command environment.
|
|
104
|
+
|
|
103
105
|
See [editors/intellij/README.md](editors/intellij/README.md) for detailed instructions.
|
|
104
106
|
|
|
105
107
|
### Claude Code
|
|
@@ -73,7 +73,7 @@ Project-level rules are configured via `.java-functional-lsp.json` in your proje
|
|
|
73
73
|
|
|
74
74
|
## Coexistence with IntelliJ's Java Support
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
The server automatically detects JetBrains IDEs and disables the jdtls proxy — IntelliJ provides its own Java language features (completions, hover, go-to-definition, compile errors). Only the 16 custom functional programming rules run, and they appear alongside IntelliJ's built-in inspections.
|
|
77
77
|
|
|
78
78
|
## Troubleshooting
|
|
79
79
|
|
|
@@ -85,6 +85,7 @@ LSP4IJ is designed to **supplement** IntelliJ's native Java support, not replace
|
|
|
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
87
|
- Try restarting the language server: **Tools** → **Language Servers** → **Restart**
|
|
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`.
|
|
88
89
|
|
|
89
90
|
### PATH not found
|
|
90
91
|
- IntelliJ may not inherit your shell's PATH. Use the full absolute path to the binary in the server command.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "java-functional-lsp"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.1"
|
|
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" }
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import json
|
|
11
11
|
import logging
|
|
12
|
+
import os
|
|
12
13
|
import re
|
|
13
14
|
import sys
|
|
14
15
|
from pathlib import Path
|
|
@@ -46,6 +47,10 @@ _ANALYZERS: list[Analyzer] = [
|
|
|
46
47
|
FunctionalChecker(),
|
|
47
48
|
]
|
|
48
49
|
|
|
50
|
+
# JetBrains IDE detection — all products contain one of these substrings in clientInfo.name.
|
|
51
|
+
# When detected, jdtls is skipped because IntelliJ provides native Java language support.
|
|
52
|
+
_JDTLS_SKIP_CLIENTS = ("IntelliJ", "JetBrains")
|
|
53
|
+
|
|
49
54
|
#: LSP-aware cattrs converter. Unstructures to the LSP JSON shape
|
|
50
55
|
#: (camelCase field names, discriminated unions, None-field pruning) and
|
|
51
56
|
#: correspondingly structures from the same shape. Using a vanilla
|
|
@@ -66,6 +71,9 @@ class JavaFunctionalLspServer(LanguageServer):
|
|
|
66
71
|
self._init_params: dict[str, Any] = {}
|
|
67
72
|
self._proxy = JdtlsProxy(on_diagnostics=self._on_jdtls_diagnostics)
|
|
68
73
|
self._user_suppress_patterns: list[re.Pattern[str]] = []
|
|
74
|
+
self._skip_jdtls: bool = False
|
|
75
|
+
self._skip_jdtls_registration: bool = False
|
|
76
|
+
self._init_generation: int = 0
|
|
69
77
|
|
|
70
78
|
def _on_jdtls_diagnostics(self, uri: str, diagnostics: list[Any]) -> None:
|
|
71
79
|
"""Called when jdtls publishes diagnostics — merge with custom and re-publish.
|
|
@@ -199,6 +207,9 @@ def _jdtls_raw_to_lsp_diagnostics(raw_diagnostics: list[Any]) -> list[lsp.Diagno
|
|
|
199
207
|
return result
|
|
200
208
|
|
|
201
209
|
|
|
210
|
+
_MAX_PATTERN_LENGTH = 500 # Cap regex length to mitigate ReDoS from pathological patterns
|
|
211
|
+
|
|
212
|
+
|
|
202
213
|
def _compile_user_patterns(config: dict[str, Any]) -> list[re.Pattern[str]]:
|
|
203
214
|
"""Compile user-defined suppressJdtlsPatterns from config."""
|
|
204
215
|
raw = config.get("suppressJdtlsPatterns", [])
|
|
@@ -209,6 +220,9 @@ def _compile_user_patterns(config: dict[str, Any]) -> list[re.Pattern[str]]:
|
|
|
209
220
|
for entry in raw:
|
|
210
221
|
if not isinstance(entry, str):
|
|
211
222
|
continue
|
|
223
|
+
if len(entry) > _MAX_PATTERN_LENGTH:
|
|
224
|
+
logger.warning("suppressJdtlsPatterns entry too long (%d chars, max %d)", len(entry), _MAX_PATTERN_LENGTH)
|
|
225
|
+
continue
|
|
212
226
|
try:
|
|
213
227
|
patterns.append(re.compile(entry))
|
|
214
228
|
except re.error as e:
|
|
@@ -276,6 +290,38 @@ def on_initialize(params: lsp.InitializeParams) -> lsp.InitializeResult:
|
|
|
276
290
|
server._config = _load_config(root)
|
|
277
291
|
server._user_suppress_patterns = _compile_user_patterns(server._config)
|
|
278
292
|
|
|
293
|
+
# Determine jdtls mode: env var takes priority, then auto-detect JetBrains IDEs.
|
|
294
|
+
client_name = (params.client_info.name if params.client_info else "")[:200]
|
|
295
|
+
logger.info("LSP client: %s", client_name or "(unknown)")
|
|
296
|
+
|
|
297
|
+
# Reset flags so re-initialization (non-standard but defensive) starts clean.
|
|
298
|
+
# Bump generation so in-flight _register_jdtls_capabilities from a prior
|
|
299
|
+
# session detects the stale context and aborts.
|
|
300
|
+
global _jdtls_capabilities_registered
|
|
301
|
+
server._skip_jdtls = False
|
|
302
|
+
server._skip_jdtls_registration = False
|
|
303
|
+
server._init_generation += 1
|
|
304
|
+
_jdtls_capabilities_registered = False
|
|
305
|
+
|
|
306
|
+
jdtls_override = os.environ.get("JAVA_FUNCTIONAL_LSP_JDTLS", "").strip().lower()
|
|
307
|
+
if jdtls_override == "off":
|
|
308
|
+
server._skip_jdtls = True
|
|
309
|
+
logger.info("jdtls proxy disabled via JAVA_FUNCTIONAL_LSP_JDTLS=off")
|
|
310
|
+
elif jdtls_override == "on":
|
|
311
|
+
logger.info("jdtls proxy force-enabled via JAVA_FUNCTIONAL_LSP_JDTLS=on")
|
|
312
|
+
elif jdtls_override == "no-register":
|
|
313
|
+
server._skip_jdtls_registration = True
|
|
314
|
+
logger.info("jdtls dynamic registration disabled via JAVA_FUNCTIONAL_LSP_JDTLS=no-register")
|
|
315
|
+
elif jdtls_override:
|
|
316
|
+
logger.warning("Unknown JAVA_FUNCTIONAL_LSP_JDTLS value %r; expected off/on/no-register", jdtls_override)
|
|
317
|
+
elif any(token in client_name for token in _JDTLS_SKIP_CLIENTS):
|
|
318
|
+
server._skip_jdtls = True
|
|
319
|
+
logger.info(
|
|
320
|
+
"JetBrains IDE detected (%s) — skipping jdtls proxy "
|
|
321
|
+
"(IDE provides native Java support; override with JAVA_FUNCTIONAL_LSP_JDTLS=on)",
|
|
322
|
+
client_name,
|
|
323
|
+
)
|
|
324
|
+
|
|
279
325
|
return lsp.InitializeResult(
|
|
280
326
|
capabilities=lsp.ServerCapabilities(
|
|
281
327
|
text_document_sync=lsp.TextDocumentSyncOptions(
|
|
@@ -302,6 +348,9 @@ async def on_initialized(params: lsp.InitializedParams) -> None:
|
|
|
302
348
|
"java-functional-lsp initialized (rules: %s)",
|
|
303
349
|
list(server._config.get("rules", {}).keys()) or "all defaults",
|
|
304
350
|
)
|
|
351
|
+
if server._skip_jdtls:
|
|
352
|
+
logger.info("jdtls proxy disabled — custom rules only")
|
|
353
|
+
return
|
|
305
354
|
if server._proxy.check_available():
|
|
306
355
|
logger.info("jdtls found on PATH — will start lazily on first file open")
|
|
307
356
|
else:
|
|
@@ -349,11 +398,15 @@ async def _register_jdtls_capabilities() -> None:
|
|
|
349
398
|
ready, which suppresses the IDE's built-in diagnostic tooltips.
|
|
350
399
|
|
|
351
400
|
Idempotent: safe to call multiple times (e.g., proxy restart).
|
|
401
|
+
Uses a generation counter to detect stale registrations from a prior
|
|
402
|
+
initialize cycle (defensive against non-standard re-initialization).
|
|
352
403
|
"""
|
|
353
404
|
global _jdtls_capabilities_registered
|
|
354
405
|
if _jdtls_capabilities_registered:
|
|
355
406
|
return
|
|
356
407
|
|
|
408
|
+
generation = server._init_generation
|
|
409
|
+
|
|
357
410
|
try:
|
|
358
411
|
# Register handlers so pygls dispatches incoming requests to them.
|
|
359
412
|
for method, handler in _JDTLS_HANDLERS.items():
|
|
@@ -362,6 +415,12 @@ async def _register_jdtls_capabilities() -> None:
|
|
|
362
415
|
# Tell the client we now support these capabilities.
|
|
363
416
|
registrations = _build_jdtls_registrations()
|
|
364
417
|
await server.client_register_capability_async(lsp.RegistrationParams(registrations=registrations))
|
|
418
|
+
|
|
419
|
+
# Bail if a re-initialize happened while we were awaiting.
|
|
420
|
+
if generation != server._init_generation:
|
|
421
|
+
logger.info("Discarding stale jdtls capability registration (re-initialize detected)")
|
|
422
|
+
return
|
|
423
|
+
|
|
365
424
|
_jdtls_capabilities_registered = True
|
|
366
425
|
logger.info("Dynamically registered jdtls capabilities (hover, definition, references, completion, symbol)")
|
|
367
426
|
except Exception:
|
|
@@ -389,6 +448,8 @@ async def _deferred_validate(uri: str) -> None:
|
|
|
389
448
|
|
|
390
449
|
def _forward_or_queue(method: str, serialized: Any) -> None:
|
|
391
450
|
"""Forward a notification to jdtls if available, or queue it if starting."""
|
|
451
|
+
if server._skip_jdtls:
|
|
452
|
+
return
|
|
392
453
|
if server._proxy.is_available:
|
|
393
454
|
task = asyncio.create_task(server._proxy.send_notification(method, serialized))
|
|
394
455
|
_bg_tasks.add(task)
|
|
@@ -406,14 +467,18 @@ async def on_did_open(params: lsp.DidOpenTextDocumentParams) -> None:
|
|
|
406
467
|
didOpen response isn't delayed by jdtls cold-start.
|
|
407
468
|
"""
|
|
408
469
|
uri = params.text_document.uri
|
|
409
|
-
serialized = _serialize_params(params)
|
|
410
470
|
|
|
411
|
-
if server.
|
|
471
|
+
if server._skip_jdtls:
|
|
472
|
+
# Skip all jdtls forwarding — custom diagnostics only.
|
|
473
|
+
pass
|
|
474
|
+
elif server._proxy.is_available:
|
|
412
475
|
# Fast path: jdtls running. Forward didOpen + add module if new.
|
|
476
|
+
serialized = _serialize_params(params)
|
|
413
477
|
await server._proxy.send_notification("textDocument/didOpen", serialized)
|
|
414
478
|
await server._proxy.add_module_if_new(uri)
|
|
415
479
|
elif server._proxy._jdtls_on_path and not server._proxy._start_failed:
|
|
416
480
|
# Queue the didOpen (whether this is the first file or a subsequent one during startup).
|
|
481
|
+
serialized = _serialize_params(params)
|
|
417
482
|
server._proxy.queue_notification("textDocument/didOpen", serialized)
|
|
418
483
|
if not server._proxy._lazy_start_fired:
|
|
419
484
|
# First file: kick off lazy start in background.
|
|
@@ -474,7 +539,10 @@ async def _lazy_start_jdtls(file_uri: str) -> None:
|
|
|
474
539
|
started = await server._proxy.ensure_started(server._init_params, file_uri, config=server._config)
|
|
475
540
|
if started:
|
|
476
541
|
logger.info("jdtls proxy active — full Java language support enabled")
|
|
477
|
-
|
|
542
|
+
if server._skip_jdtls_registration:
|
|
543
|
+
logger.info("Skipping dynamic capability registration (no-register mode)")
|
|
544
|
+
else:
|
|
545
|
+
await _register_jdtls_capabilities()
|
|
478
546
|
await server._proxy.flush_queued_notifications()
|
|
479
547
|
except Exception:
|
|
480
548
|
logger.warning("jdtls lazy start failed", exc_info=True)
|
|
@@ -750,6 +750,100 @@ class TestJdtlsIsolation:
|
|
|
750
750
|
# If we get here without exception, the handler caught it correctly
|
|
751
751
|
|
|
752
752
|
|
|
753
|
+
class TestJdtlsSkipDetection:
|
|
754
|
+
"""Tests for auto-detecting JetBrains IDEs and skipping jdtls."""
|
|
755
|
+
|
|
756
|
+
def _make_init_params(self, client_name: str | None = None) -> Any:
|
|
757
|
+
"""Create minimal InitializeParams with optional client_info."""
|
|
758
|
+
return lsp.InitializeParams(
|
|
759
|
+
capabilities=lsp.ClientCapabilities(),
|
|
760
|
+
root_uri="file:///tmp/test",
|
|
761
|
+
client_info=lsp.ClientInfo(name=client_name) if client_name else None,
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
def _run_init(self, monkeypatch: Any, client_name: str | None, env_value: str | None = None) -> None:
|
|
765
|
+
"""Run on_initialize with controlled env and client_info."""
|
|
766
|
+
from java_functional_lsp.server import on_initialize, server
|
|
767
|
+
|
|
768
|
+
server._skip_jdtls = False
|
|
769
|
+
server._skip_jdtls_registration = False
|
|
770
|
+
if env_value is not None:
|
|
771
|
+
monkeypatch.setenv("JAVA_FUNCTIONAL_LSP_JDTLS", env_value)
|
|
772
|
+
else:
|
|
773
|
+
monkeypatch.delenv("JAVA_FUNCTIONAL_LSP_JDTLS", raising=False)
|
|
774
|
+
on_initialize(self._make_init_params(client_name))
|
|
775
|
+
|
|
776
|
+
def test_intellij_client_info_skips_jdtls(self, monkeypatch: Any) -> None:
|
|
777
|
+
"""IntelliJ IDEA detected → _skip_jdtls = True."""
|
|
778
|
+
from java_functional_lsp.server import server
|
|
779
|
+
|
|
780
|
+
self._run_init(monkeypatch, "IntelliJ IDEA")
|
|
781
|
+
assert server._skip_jdtls is True
|
|
782
|
+
|
|
783
|
+
def test_jetbrains_product_skips_jdtls(self, monkeypatch: Any) -> None:
|
|
784
|
+
"""Any JetBrains product detected → _skip_jdtls = True."""
|
|
785
|
+
from java_functional_lsp.server import server
|
|
786
|
+
|
|
787
|
+
self._run_init(monkeypatch, "JetBrains Rider 2025.2")
|
|
788
|
+
assert server._skip_jdtls is True
|
|
789
|
+
|
|
790
|
+
def test_vscode_allows_jdtls(self, monkeypatch: Any) -> None:
|
|
791
|
+
"""VS Code → _skip_jdtls = False (jdtls needed)."""
|
|
792
|
+
from java_functional_lsp.server import server
|
|
793
|
+
|
|
794
|
+
self._run_init(monkeypatch, "Visual Studio Code")
|
|
795
|
+
assert server._skip_jdtls is False
|
|
796
|
+
|
|
797
|
+
def test_no_client_info_allows_jdtls(self, monkeypatch: Any) -> None:
|
|
798
|
+
"""No client_info → _skip_jdtls = False (safe default)."""
|
|
799
|
+
from java_functional_lsp.server import server
|
|
800
|
+
|
|
801
|
+
self._run_init(monkeypatch, None)
|
|
802
|
+
assert server._skip_jdtls is False
|
|
803
|
+
|
|
804
|
+
def test_env_var_off_overrides_detection(self, monkeypatch: Any) -> None:
|
|
805
|
+
"""JAVA_FUNCTIONAL_LSP_JDTLS=off disables jdtls even for non-JetBrains client."""
|
|
806
|
+
from java_functional_lsp.server import server
|
|
807
|
+
|
|
808
|
+
self._run_init(monkeypatch, "Visual Studio Code", env_value="off")
|
|
809
|
+
assert server._skip_jdtls is True
|
|
810
|
+
|
|
811
|
+
def test_env_var_on_overrides_intellij_detection(self, monkeypatch: Any) -> None:
|
|
812
|
+
"""JAVA_FUNCTIONAL_LSP_JDTLS=on force-enables jdtls even for IntelliJ."""
|
|
813
|
+
from java_functional_lsp.server import server
|
|
814
|
+
|
|
815
|
+
self._run_init(monkeypatch, "IntelliJ IDEA", env_value="on")
|
|
816
|
+
assert server._skip_jdtls is False
|
|
817
|
+
|
|
818
|
+
def test_env_var_no_register(self, monkeypatch: Any) -> None:
|
|
819
|
+
"""JAVA_FUNCTIONAL_LSP_JDTLS=no-register keeps jdtls but skips registration."""
|
|
820
|
+
from java_functional_lsp.server import server
|
|
821
|
+
|
|
822
|
+
self._run_init(monkeypatch, "Visual Studio Code", env_value="no-register")
|
|
823
|
+
assert server._skip_jdtls is False
|
|
824
|
+
assert server._skip_jdtls_registration is True
|
|
825
|
+
|
|
826
|
+
def test_custom_diagnostics_publish_when_jdtls_skipped(self) -> None:
|
|
827
|
+
"""Tree-sitter analysis works normally when jdtls is skipped."""
|
|
828
|
+
from unittest.mock import patch
|
|
829
|
+
|
|
830
|
+
from java_functional_lsp.server import _run_analysis
|
|
831
|
+
|
|
832
|
+
java_source = "public class Foo { public String bar() { return null; } }"
|
|
833
|
+
|
|
834
|
+
with patch("java_functional_lsp.server.server") as mock_server:
|
|
835
|
+
mock_server._proxy.is_available = False
|
|
836
|
+
mock_server._skip_jdtls = True
|
|
837
|
+
mock_server._config = {}
|
|
838
|
+
mock_server._parser = __import__("java_functional_lsp.analyzers.base", fromlist=["get_parser"]).get_parser()
|
|
839
|
+
|
|
840
|
+
result = _run_analysis(java_source, "file:///test/Foo.java")
|
|
841
|
+
|
|
842
|
+
custom_diags = [d for d in result if d.source == "java-functional-lsp"]
|
|
843
|
+
assert len(custom_diags) > 0, "Custom diagnostics should publish when jdtls is skipped"
|
|
844
|
+
assert any(d.code == "null-return" for d in custom_diags), "null-return diagnostic expected"
|
|
845
|
+
|
|
846
|
+
|
|
753
847
|
class TestFindLombokJar:
|
|
754
848
|
"""Tests for _find_lombok_jar()."""
|
|
755
849
|
|
|
@@ -184,7 +184,7 @@ wheels = [
|
|
|
184
184
|
|
|
185
185
|
[[package]]
|
|
186
186
|
name = "java-functional-lsp"
|
|
187
|
-
version = "0.8.
|
|
187
|
+
version = "0.8.1"
|
|
188
188
|
source = { editable = "." }
|
|
189
189
|
dependencies = [
|
|
190
190
|
{ name = "pygls" },
|
|
@@ -426,7 +426,7 @@ wheels = [
|
|
|
426
426
|
|
|
427
427
|
[[package]]
|
|
428
428
|
name = "pytest"
|
|
429
|
-
version = "9.0.
|
|
429
|
+
version = "9.0.3"
|
|
430
430
|
source = { registry = "https://pypi.org/simple" }
|
|
431
431
|
dependencies = [
|
|
432
432
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
@@ -437,9 +437,9 @@ dependencies = [
|
|
|
437
437
|
{ name = "pygments" },
|
|
438
438
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
439
439
|
]
|
|
440
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
440
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
|
441
441
|
wheels = [
|
|
442
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
442
|
+
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
|
443
443
|
]
|
|
444
444
|
|
|
445
445
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.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.8.0 → java_functional_lsp-0.8.1}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/.github/workflows/update-homebrew.yml
RENAMED
|
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.8.0 → java_functional_lsp-0.8.1}/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
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.8.0 → java_functional_lsp-0.8.1}/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
|