java-functional-lsp 0.9.7__tar.gz → 0.9.9__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.7 → java_functional_lsp-0.9.9}/PKG-INFO +1 -1
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/pyproject.toml +1 -1
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/server.py +39 -11
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_server.py +49 -1
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.claude-plugin/plugin.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.gitignore +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/LICENSE +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/README.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/SKILL.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/merkle.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/src/java_functional_lsp/proxy.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/__init__.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/conftest.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_base.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_config.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_merkle.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_merkle_proxy.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/tests/test_suppress.py +0 -0
- {java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
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.9.
|
|
7
|
+
version = "0.9.9"
|
|
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" }
|
|
@@ -76,13 +76,18 @@ class JavaFunctionalLspServer(LanguageServer):
|
|
|
76
76
|
self._skip_jdtls_registration: bool = False
|
|
77
77
|
self._init_generation: int = 0
|
|
78
78
|
|
|
79
|
-
def _on_jdtls_diagnostics(self, uri: str,
|
|
79
|
+
def _on_jdtls_diagnostics(self, uri: str, diagnostics: list[Any]) -> None:
|
|
80
80
|
"""Called when jdtls publishes diagnostics — merge with custom and re-publish.
|
|
81
81
|
|
|
82
82
|
Also marks the file's module as READY, since receiving diagnostics from
|
|
83
83
|
jdtls is a reliable signal that the module has been indexed (more reliable
|
|
84
84
|
than a first non-None response which may be semantically empty).
|
|
85
85
|
|
|
86
|
+
Exception: if any diagnostic carries ``_JDTLS_NON_PROJECT_CODE`` (code 16),
|
|
87
|
+
jdtls has opened the file but hasn't resolved its Maven project yet — only
|
|
88
|
+
syntax errors are available. We skip the READY transition and wait for the
|
|
89
|
+
follow-up publish (after Maven import completes) that arrives without code 16.
|
|
90
|
+
|
|
86
91
|
When a module transitions to READY, fires ``_apply_module_diff`` to
|
|
87
92
|
notify jdtls about any externally changed files (e.g., after git pull).
|
|
88
93
|
"""
|
|
@@ -90,12 +95,18 @@ class JavaFunctionalLspServer(LanguageServer):
|
|
|
90
95
|
return
|
|
91
96
|
module_uri = _resolve_module_uri(uri)
|
|
92
97
|
if module_uri:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
is_non_project = any(
|
|
99
|
+
str(d.get("code", "")) == _JDTLS_NON_PROJECT_CODE for d in diagnostics if isinstance(d, dict)
|
|
100
|
+
)
|
|
101
|
+
if is_non_project:
|
|
102
|
+
logger.info("jdtls: skipping READY for %s (non-project file, Maven import pending)", Path(uri).name)
|
|
103
|
+
else:
|
|
104
|
+
already_ready = self._proxy.modules.is_ready(module_uri)
|
|
105
|
+
self._proxy.modules.mark_ready(module_uri)
|
|
106
|
+
if not already_ready:
|
|
107
|
+
# First READY transition only — subsequent diagnostics for the same
|
|
108
|
+
# module are no-ops to avoid spawning O(files) redundant tasks.
|
|
109
|
+
_fire_and_forget(_apply_module_diff(self._proxy, module_uri))
|
|
99
110
|
try:
|
|
100
111
|
_analyze_and_publish(uri)
|
|
101
112
|
except FileNotFoundError:
|
|
@@ -419,7 +430,7 @@ def on_initialize(params: lsp.InitializeParams) -> lsp.InitializeResult:
|
|
|
419
430
|
|
|
420
431
|
|
|
421
432
|
@server.feature(lsp.INITIALIZED)
|
|
422
|
-
async def on_initialized(
|
|
433
|
+
async def on_initialized(_: lsp.InitializedParams) -> None:
|
|
423
434
|
"""Check jdtls availability; actual start deferred to first didOpen."""
|
|
424
435
|
logger.info(
|
|
425
436
|
"java-functional-lsp initialized (rules: %s)",
|
|
@@ -672,6 +683,12 @@ _LIGHTWEIGHT_METHODS: frozenset[str] = frozenset(
|
|
|
672
683
|
}
|
|
673
684
|
)
|
|
674
685
|
|
|
686
|
+
# jdtls diagnostic code for files it opened but hasn't resolved into a Maven project yet.
|
|
687
|
+
# When this appears in a publishDiagnostics batch, jdtls has syntax info only — no classpath,
|
|
688
|
+
# no cross-file resolution. We must NOT mark the module READY yet; wait for a re-publish
|
|
689
|
+
# without code 16 (after Maven project import finishes).
|
|
690
|
+
_JDTLS_NON_PROJECT_CODE: str = "16"
|
|
691
|
+
|
|
675
692
|
|
|
676
693
|
async def _ensure_module_and_forward(
|
|
677
694
|
method: str,
|
|
@@ -696,12 +713,18 @@ async def _ensure_module_and_forward(
|
|
|
696
713
|
return None
|
|
697
714
|
|
|
698
715
|
module_uri = _resolve_module_uri(file_uri)
|
|
716
|
+
short_mod = Path(to_fs_path(module_uri) or module_uri).name if module_uri else "<none>"
|
|
699
717
|
|
|
700
718
|
# Hot path: module already confirmed working.
|
|
701
719
|
if module_uri and proxy.modules.is_ready(module_uri):
|
|
702
|
-
|
|
720
|
+
logger.info("jdtls: %s [hot] module=%s", method, short_mod)
|
|
721
|
+
result = await proxy.send_request(method, _serialize_params(params))
|
|
722
|
+
if result is None:
|
|
723
|
+
logger.info("jdtls: %s [hot] → null (jdtls has no result for this file_uri)", method)
|
|
724
|
+
return result
|
|
703
725
|
|
|
704
726
|
# Cold path: add module if unknown, then wait for ready.
|
|
727
|
+
logger.info("jdtls: %s [cold] module=%s expanded=%s", method, short_mod, proxy._workspace_expanded)
|
|
705
728
|
new_module_uri = await proxy.add_module_if_new(file_uri)
|
|
706
729
|
|
|
707
730
|
serialized = _serialize_params(params)
|
|
@@ -719,11 +742,13 @@ async def _ensure_module_and_forward(
|
|
|
719
742
|
# If a concurrent request succeeds, Event.set() wakes us early.
|
|
720
743
|
wait_uri = new_module_uri or module_uri
|
|
721
744
|
if wait_uri and not proxy.modules.is_ready(wait_uri):
|
|
745
|
+
logger.info("jdtls: %s [cold] waiting for module %s to become ready", method, short_mod)
|
|
722
746
|
await proxy.modules.wait_until_ready(wait_uri, timeout=5.0)
|
|
723
747
|
# Always retry once after waiting — even on timeout the module may be ready.
|
|
724
748
|
result = await proxy.send_request(method, serialized)
|
|
725
749
|
if result is not None and can_mark_ready and module_uri:
|
|
726
750
|
proxy.modules.mark_ready(module_uri)
|
|
751
|
+
logger.info("jdtls: %s [cold] retry → %s", method, "result" if result is not None else "null")
|
|
727
752
|
return result
|
|
728
753
|
|
|
729
754
|
|
|
@@ -803,8 +828,11 @@ async def _on_incoming_calls(
|
|
|
803
828
|
if result is None:
|
|
804
829
|
return None
|
|
805
830
|
try:
|
|
806
|
-
|
|
807
|
-
|
|
831
|
+
structured = [_converter.structure(c, lsp.CallHierarchyIncomingCall) for c in result]
|
|
832
|
+
logger.info("jdtls: incomingCalls → %d callers", len(structured))
|
|
833
|
+
return structured
|
|
834
|
+
except Exception as e:
|
|
835
|
+
logger.warning("jdtls: incomingCalls structuring failed: %s (raw=%r)", e, result)
|
|
808
836
|
return None
|
|
809
837
|
|
|
810
838
|
|
|
@@ -290,6 +290,54 @@ class TestServerInternals:
|
|
|
290
290
|
finally:
|
|
291
291
|
server.workspace.remove_text_document(uri)
|
|
292
292
|
|
|
293
|
+
def test_on_jdtls_diagnostics_non_project_skips_ready(self) -> None:
|
|
294
|
+
"""A publishDiagnostics batch with code-16 must NOT mark the module READY."""
|
|
295
|
+
from unittest.mock import patch
|
|
296
|
+
|
|
297
|
+
from java_functional_lsp.proxy import ModuleState
|
|
298
|
+
from java_functional_lsp.server import server
|
|
299
|
+
|
|
300
|
+
server._proxy.modules.clear()
|
|
301
|
+
uri = "file:///mod/Foo.java"
|
|
302
|
+
non_project_diag = {
|
|
303
|
+
"code": "16",
|
|
304
|
+
"message": "Foo.java is a non-project file, only syntax errors are reported",
|
|
305
|
+
"source": "Java",
|
|
306
|
+
"severity": 2,
|
|
307
|
+
"range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}},
|
|
308
|
+
}
|
|
309
|
+
with (
|
|
310
|
+
patch("java_functional_lsp.server._resolve_module_uri", return_value="file:///mod"),
|
|
311
|
+
patch("java_functional_lsp.server._analyze_and_publish"),
|
|
312
|
+
):
|
|
313
|
+
server._on_jdtls_diagnostics(uri, [non_project_diag])
|
|
314
|
+
assert server._proxy.modules.get_state("file:///mod") != ModuleState.READY
|
|
315
|
+
|
|
316
|
+
def test_on_jdtls_diagnostics_project_file_marks_ready(self) -> None:
|
|
317
|
+
"""A publishDiagnostics batch without code-16 MUST mark the module READY."""
|
|
318
|
+
from unittest.mock import patch
|
|
319
|
+
|
|
320
|
+
from java_functional_lsp.proxy import ModuleState
|
|
321
|
+
from java_functional_lsp.server import server
|
|
322
|
+
|
|
323
|
+
server._proxy.modules.clear()
|
|
324
|
+
uri = "file:///mod/Pr.java"
|
|
325
|
+
real_diag = {
|
|
326
|
+
"code": "268435844",
|
|
327
|
+
"message": "Some unused import",
|
|
328
|
+
"source": "Java",
|
|
329
|
+
"severity": 2,
|
|
330
|
+
"range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}},
|
|
331
|
+
}
|
|
332
|
+
with (
|
|
333
|
+
patch("java_functional_lsp.server._resolve_module_uri", return_value="file:///mod"),
|
|
334
|
+
patch("java_functional_lsp.server._analyze_and_publish"),
|
|
335
|
+
patch("java_functional_lsp.server._fire_and_forget"),
|
|
336
|
+
):
|
|
337
|
+
server._on_jdtls_diagnostics(uri, [real_diag])
|
|
338
|
+
assert server._proxy.modules.get_state("file:///mod") == ModuleState.READY
|
|
339
|
+
server._proxy.modules.clear()
|
|
340
|
+
|
|
293
341
|
def test_init_capabilities_exclude_jdtls_features(self) -> None:
|
|
294
342
|
"""Static capabilities must NOT include hover/definition/references/completion/documentSymbol.
|
|
295
343
|
|
|
@@ -541,7 +589,7 @@ class TestServerInternals:
|
|
|
541
589
|
mock_add = AsyncMock(return_value="file:///mod")
|
|
542
590
|
mock_send = AsyncMock(side_effect=[None, {"result": "ok"}])
|
|
543
591
|
|
|
544
|
-
async def mock_wait(uri: str, timeout: float = 30.0) -> bool: #
|
|
592
|
+
async def mock_wait(uri: str, timeout: float = 30.0) -> bool: # type: ignore[reportUnusedParameter]
|
|
545
593
|
srv._proxy.modules.mark_ready(uri)
|
|
546
594
|
return True
|
|
547
595
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.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.7 → java_functional_lsp-0.9.9}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.9.7 → java_functional_lsp-0.9.9}/.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.9.7 → java_functional_lsp-0.9.9}/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.9.7 → java_functional_lsp-0.9.9}/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
|
|
File without changes
|