java-functional-lsp 0.7.6__tar.gz → 0.7.8__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.8}/PKG-INFO +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/pyproject.toml +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/proxy.py +25 -7
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/server.py +12 -4
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_server.py +1 -4
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.claude-plugin/plugin.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.gitignore +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/LICENSE +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/SKILL.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/__init__.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/conftest.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_base.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_config.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/tests/test_suppress.py +0 -0
- {java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/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.8
|
|
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.8"
|
|
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)
|
|
@@ -481,18 +484,22 @@ class JdtlsProxy:
|
|
|
481
484
|
"""Start jdtls subprocess and initialize it.
|
|
482
485
|
|
|
483
486
|
If *module_root_uri* is provided, jdtls is scoped to that module for
|
|
484
|
-
fast startup
|
|
485
|
-
|
|
487
|
+
fast startup and the data-dir hash is based on the module URI (so each
|
|
488
|
+
module gets its own isolated index). Otherwise the workspace root is used.
|
|
486
489
|
"""
|
|
487
490
|
jdtls_path = shutil.which("jdtls")
|
|
488
491
|
if not jdtls_path:
|
|
489
492
|
return False
|
|
490
493
|
|
|
491
|
-
# Data-dir hash
|
|
494
|
+
# Data-dir hash: use module URI when scoped so each module gets its own
|
|
495
|
+
# clean jdtls state. This avoids loading a 2.5GB monorepo index when only
|
|
496
|
+
# one module is needed. Without expansion, each session is module-scoped,
|
|
497
|
+
# so the data-dir should match that scope.
|
|
492
498
|
original_root: str = init_params.get("rootUri") or init_params.get("rootPath") or str(Path.cwd())
|
|
493
499
|
self._original_root_uri = original_root
|
|
494
|
-
|
|
495
|
-
|
|
500
|
+
hash_source = module_root_uri or original_root
|
|
501
|
+
data_hash = hashlib.sha256(hash_source.encode()).hexdigest()[:12]
|
|
502
|
+
data_dir = Path.home() / ".cache" / "jdtls-data" / data_hash
|
|
496
503
|
data_dir.mkdir(parents=True, exist_ok=True)
|
|
497
504
|
|
|
498
505
|
# Deep copy to avoid mutating server._init_params.
|
|
@@ -566,12 +573,21 @@ class JdtlsProxy:
|
|
|
566
573
|
"""Start jdtls lazily, scoped to the module containing *file_uri*.
|
|
567
574
|
|
|
568
575
|
Thread-safe: uses asyncio.Lock to prevent double-start from rapid
|
|
569
|
-
didOpen calls. Sets ``_start_failed`` on failure to prevent retries
|
|
576
|
+
didOpen calls. Sets ``_start_failed`` on failure to prevent retries,
|
|
577
|
+
but allows retry after a cooldown period (5 minutes) so transient
|
|
578
|
+
failures (Maven Central timeout, JVM OOM) don't permanently disable jdtls.
|
|
570
579
|
"""
|
|
571
580
|
if self._available:
|
|
572
581
|
return True
|
|
573
|
-
if
|
|
582
|
+
if not self._jdtls_on_path:
|
|
574
583
|
return False
|
|
584
|
+
if self._start_failed:
|
|
585
|
+
if self._start_failed_at and (time.monotonic() - self._start_failed_at > _START_RETRY_COOLDOWN):
|
|
586
|
+
self._start_failed = False
|
|
587
|
+
self._start_failed_at = None
|
|
588
|
+
logger.info("jdtls: retrying after previous failure (cooldown elapsed)")
|
|
589
|
+
else:
|
|
590
|
+
return False
|
|
575
591
|
|
|
576
592
|
async with self._start_lock:
|
|
577
593
|
if self._available:
|
|
@@ -583,10 +599,12 @@ class JdtlsProxy:
|
|
|
583
599
|
started = await self.start(init_params, module_root_uri=module_uri)
|
|
584
600
|
if not started:
|
|
585
601
|
self._start_failed = True
|
|
602
|
+
self._start_failed_at = time.monotonic()
|
|
586
603
|
self._queued_notifications.clear()
|
|
587
604
|
return started
|
|
588
605
|
except Exception:
|
|
589
606
|
self._start_failed = True
|
|
607
|
+
self._start_failed_at = time.monotonic()
|
|
590
608
|
self._queued_notifications.clear()
|
|
591
609
|
raise
|
|
592
610
|
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.8}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.7.6 → java_functional_lsp-0.7.8}/.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.8}/.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.8}/.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.8}/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.8}/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
|