java-functional-lsp 0.7.6__tar.gz → 0.7.7__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.7.6 → java_functional_lsp-0.7.7}/PKG-INFO +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/pyproject.toml +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/proxy.py +16 -2
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/server.py +12 -4
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_server.py +1 -4
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.claude-plugin/plugin.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.gitignore +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/LICENSE +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/SKILL.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/__init__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/conftest.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_base.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_config.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/tests/test_suppress.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.7
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "java-functional-lsp"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.7"
|
|
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" }
|
|
@@ -12,6 +12,7 @@ import platform
|
|
|
12
12
|
import re
|
|
13
13
|
import shutil
|
|
14
14
|
import subprocess
|
|
15
|
+
import time
|
|
15
16
|
from collections import deque
|
|
16
17
|
from collections.abc import Callable, Mapping
|
|
17
18
|
from functools import lru_cache
|
|
@@ -22,6 +23,7 @@ logger = logging.getLogger(__name__)
|
|
|
22
23
|
|
|
23
24
|
REQUEST_TIMEOUT = 30.0 # seconds — per-request timeout for normal operations
|
|
24
25
|
_INITIALIZE_TIMEOUT = 120.0 # seconds — module-scoped init can still be slow (Maven classpath resolution)
|
|
26
|
+
_START_RETRY_COOLDOWN = 300.0 # seconds — retry jdtls startup after transient failure
|
|
25
27
|
DEFAULT_JVM_MAX_HEAP = "4g"
|
|
26
28
|
_STDERR_LINE_MAX = 1000
|
|
27
29
|
|
|
@@ -448,6 +450,7 @@ class JdtlsProxy:
|
|
|
448
450
|
self._start_lock = asyncio.Lock()
|
|
449
451
|
self._starting = False
|
|
450
452
|
self._start_failed = False
|
|
453
|
+
self._start_failed_at: float | None = None
|
|
451
454
|
self._jdtls_on_path = False
|
|
452
455
|
self._lazy_start_fired = False
|
|
453
456
|
self._queued_notifications: deque[tuple[str, Any]] = deque(maxlen=_MAX_QUEUED_NOTIFICATIONS)
|
|
@@ -566,12 +569,21 @@ class JdtlsProxy:
|
|
|
566
569
|
"""Start jdtls lazily, scoped to the module containing *file_uri*.
|
|
567
570
|
|
|
568
571
|
Thread-safe: uses asyncio.Lock to prevent double-start from rapid
|
|
569
|
-
didOpen calls. Sets ``_start_failed`` on failure to prevent retries
|
|
572
|
+
didOpen calls. Sets ``_start_failed`` on failure to prevent retries,
|
|
573
|
+
but allows retry after a cooldown period (5 minutes) so transient
|
|
574
|
+
failures (Maven Central timeout, JVM OOM) don't permanently disable jdtls.
|
|
570
575
|
"""
|
|
571
576
|
if self._available:
|
|
572
577
|
return True
|
|
573
|
-
if
|
|
578
|
+
if not self._jdtls_on_path:
|
|
574
579
|
return False
|
|
580
|
+
if self._start_failed:
|
|
581
|
+
if self._start_failed_at and (time.monotonic() - self._start_failed_at > _START_RETRY_COOLDOWN):
|
|
582
|
+
self._start_failed = False
|
|
583
|
+
self._start_failed_at = None
|
|
584
|
+
logger.info("jdtls: retrying after previous failure (cooldown elapsed)")
|
|
585
|
+
else:
|
|
586
|
+
return False
|
|
575
587
|
|
|
576
588
|
async with self._start_lock:
|
|
577
589
|
if self._available:
|
|
@@ -583,10 +595,12 @@ class JdtlsProxy:
|
|
|
583
595
|
started = await self.start(init_params, module_root_uri=module_uri)
|
|
584
596
|
if not started:
|
|
585
597
|
self._start_failed = True
|
|
598
|
+
self._start_failed_at = time.monotonic()
|
|
586
599
|
self._queued_notifications.clear()
|
|
587
600
|
return started
|
|
588
601
|
except Exception:
|
|
589
602
|
self._start_failed = True
|
|
603
|
+
self._start_failed_at = time.monotonic()
|
|
590
604
|
self._queued_notifications.clear()
|
|
591
605
|
raise
|
|
592
606
|
finally:
|
|
@@ -66,9 +66,17 @@ class JavaFunctionalLspServer(LanguageServer):
|
|
|
66
66
|
self._proxy = JdtlsProxy(on_diagnostics=self._on_jdtls_diagnostics)
|
|
67
67
|
|
|
68
68
|
def _on_jdtls_diagnostics(self, uri: str, diagnostics: list[Any]) -> None:
|
|
69
|
-
"""Called when jdtls publishes diagnostics — merge with custom and re-publish.
|
|
69
|
+
"""Called when jdtls publishes diagnostics — merge with custom and re-publish.
|
|
70
|
+
|
|
71
|
+
Also marks the file's module as READY, since receiving diagnostics from
|
|
72
|
+
jdtls is a reliable signal that the module has been indexed (more reliable
|
|
73
|
+
than a first non-None response which may be semantically empty).
|
|
74
|
+
"""
|
|
70
75
|
if not uri.endswith(".java"):
|
|
71
76
|
return
|
|
77
|
+
module_uri = _resolve_module_uri(uri)
|
|
78
|
+
if module_uri:
|
|
79
|
+
self._proxy.modules.mark_ready(module_uri)
|
|
72
80
|
try:
|
|
73
81
|
_analyze_and_publish(uri)
|
|
74
82
|
except Exception as e:
|
|
@@ -406,8 +414,9 @@ async def _lazy_start_jdtls(file_uri: str) -> None:
|
|
|
406
414
|
"""Background task: start jdtls scoped to the module containing *file_uri*.
|
|
407
415
|
|
|
408
416
|
Runs in the background so ``on_did_open`` returns immediately with custom
|
|
409
|
-
diagnostics. After jdtls initializes, registers capabilities
|
|
410
|
-
queued notifications
|
|
417
|
+
diagnostics. After jdtls initializes, registers capabilities and flushes
|
|
418
|
+
queued notifications. Workspace expansion is NOT done eagerly — modules
|
|
419
|
+
are loaded on-demand via ``add_module_if_new()`` as files are opened.
|
|
411
420
|
"""
|
|
412
421
|
try:
|
|
413
422
|
started = await server._proxy.ensure_started(server._init_params, file_uri)
|
|
@@ -415,7 +424,6 @@ async def _lazy_start_jdtls(file_uri: str) -> None:
|
|
|
415
424
|
logger.info("jdtls proxy active — full Java language support enabled")
|
|
416
425
|
await _register_jdtls_capabilities()
|
|
417
426
|
await server._proxy.flush_queued_notifications()
|
|
418
|
-
await _expand_workspace_background()
|
|
419
427
|
except Exception:
|
|
420
428
|
logger.warning("jdtls lazy start failed", exc_info=True)
|
|
421
429
|
|
|
@@ -415,7 +415,7 @@ class TestServerInternals:
|
|
|
415
415
|
mock_reg.assert_not_called()
|
|
416
416
|
|
|
417
417
|
async def test_lazy_start_jdtls_success(self, caplog: Any) -> None:
|
|
418
|
-
"""_lazy_start_jdtls logs success
|
|
418
|
+
"""_lazy_start_jdtls logs success and flushes queue (no eager expansion)."""
|
|
419
419
|
import logging
|
|
420
420
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
421
421
|
|
|
@@ -424,7 +424,6 @@ class TestServerInternals:
|
|
|
424
424
|
from java_functional_lsp.server import server as srv
|
|
425
425
|
|
|
426
426
|
mock_flush = AsyncMock()
|
|
427
|
-
mock_expand = AsyncMock()
|
|
428
427
|
old_flag = srv_mod._jdtls_capabilities_registered
|
|
429
428
|
srv_mod._jdtls_capabilities_registered = False
|
|
430
429
|
try:
|
|
@@ -432,7 +431,6 @@ class TestServerInternals:
|
|
|
432
431
|
caplog.at_level(logging.INFO, logger="java_functional_lsp.server"),
|
|
433
432
|
patch.object(srv._proxy, "ensure_started", AsyncMock(return_value=True)),
|
|
434
433
|
patch.object(srv._proxy, "flush_queued_notifications", mock_flush),
|
|
435
|
-
patch.object(srv._proxy, "expand_full_workspace", mock_expand),
|
|
436
434
|
patch.object(srv, "feature", MagicMock(return_value=lambda fn: fn)),
|
|
437
435
|
patch.object(srv, "client_register_capability_async", AsyncMock()),
|
|
438
436
|
):
|
|
@@ -441,7 +439,6 @@ class TestServerInternals:
|
|
|
441
439
|
srv_mod._jdtls_capabilities_registered = old_flag
|
|
442
440
|
assert any("jdtls proxy active" in r.getMessage() for r in caplog.records)
|
|
443
441
|
mock_flush.assert_called_once()
|
|
444
|
-
mock_expand.assert_called_once()
|
|
445
442
|
|
|
446
443
|
async def test_lazy_start_jdtls_failure_logged(self, caplog: Any) -> None:
|
|
447
444
|
"""_lazy_start_jdtls logs warning on exception."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.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.7.6 → java_functional_lsp-0.7.7}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/.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
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.7.6 → java_functional_lsp-0.7.7}/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.7.6 → java_functional_lsp-0.7.7}/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
|