java-functional-lsp 0.9.6__tar.gz → 0.9.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.
Files changed (69) hide show
  1. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/PKG-INFO +1 -1
  2. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/pyproject.toml +1 -1
  3. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/__init__.py +1 -1
  4. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/server.py +41 -9
  5. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_server.py +28 -0
  6. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.claude-plugin/plugin.json +0 -0
  7. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.githooks/pre-commit +0 -0
  8. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.githooks/pre-push +0 -0
  9. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/CODEOWNERS +0 -0
  10. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
  11. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  12. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  13. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/SECURITY.md +0 -0
  14. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/dependabot.yml +0 -0
  15. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/release-drafter.yml +0 -0
  16. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/publish.yml +0 -0
  17. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/release-drafter.yml +0 -0
  18. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/stale.yml +0 -0
  19. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/test.yml +0 -0
  20. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/update-homebrew.yml +0 -0
  21. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.github/workflows/vscode-ext.yml +0 -0
  22. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/.gitignore +0 -0
  23. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/CONTRIBUTING.md +0 -0
  24. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/LICENSE +0 -0
  25. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/README.md +0 -0
  26. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/SKILL.md +0 -0
  27. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/commands/lint-java.md +0 -0
  28. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/intellij/README.md +0 -0
  29. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/intellij/lsp4ij-template.json +0 -0
  30. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/.vscodeignore +0 -0
  31. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/README.md +0 -0
  32. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/package-lock.json +0 -0
  33. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/package.json +0 -0
  34. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/src/extension.ts +0 -0
  35. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/editors/vscode/tsconfig.json +0 -0
  36. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/hooks/hooks.json +0 -0
  37. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/hooks/java_linter_reminder.py +0 -0
  38. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/scripts/ensure-lsp.sh +0 -0
  39. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/scripts/generate-formula.py +0 -0
  40. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/__main__.py +0 -0
  41. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/__init__.py +0 -0
  42. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/base.py +0 -0
  43. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
  44. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
  45. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
  46. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
  47. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
  48. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/cli.py +0 -0
  49. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/fixes.py +0 -0
  50. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/merkle.py +0 -0
  51. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/src/java_functional_lsp/proxy.py +0 -0
  52. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/__init__.py +0 -0
  53. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/conftest.py +0 -0
  54. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_base.py +0 -0
  55. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_cli.py +0 -0
  56. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_config.py +0 -0
  57. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_e2e.py +0 -0
  58. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_e2e_jdtls.py +0 -0
  59. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_exception_checker.py +0 -0
  60. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_fixes.py +0 -0
  61. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_functional_checker.py +0 -0
  62. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_merkle.py +0 -0
  63. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_merkle_proxy.py +0 -0
  64. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_mutation_checker.py +0 -0
  65. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_null_checker.py +0 -0
  66. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_proxy.py +0 -0
  67. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_spring_checker.py +0 -0
  68. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/tests/test_suppress.py +0 -0
  69. {java_functional_lsp-0.9.6 → java_functional_lsp-0.9.8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-functional-lsp
3
- Version: 0.9.6
3
+ Version: 0.9.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.9.6"
7
+ version = "0.9.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" }
@@ -1,3 +1,3 @@
1
1
  """java-functional-lsp: A Java LSP server enforcing functional programming best practices."""
2
2
 
3
- __version__ = "0.9.6"
3
+ __version__ = "0.9.8"
@@ -660,7 +660,24 @@ async def _lazy_start_jdtls(file_uri: str) -> None:
660
660
  # they only activate after jdtls starts.
661
661
 
662
662
 
663
- async def _ensure_module_and_forward(method: str, params: Any, file_uri: str) -> Any | None:
663
+ # Methods that operate on a single file and succeed before cross-file indexing
664
+ # is complete. A successful response from these does NOT mean the module is fully
665
+ # indexed, so we must NOT use them to transition the module to READY — doing so
666
+ # would cause cross-file requests (references, callHierarchy) to skip the wait
667
+ # and return empty results from an under-indexed jdtls instance.
668
+ _LIGHTWEIGHT_METHODS: frozenset[str] = frozenset(
669
+ {
670
+ "textDocument/documentHighlight",
671
+ "textDocument/documentSymbol",
672
+ }
673
+ )
674
+
675
+
676
+ async def _ensure_module_and_forward(
677
+ method: str,
678
+ params: Any,
679
+ file_uri: str,
680
+ ) -> Any | None:
664
681
  """Forward a request to jdtls, ensuring the file's module is loaded.
665
682
 
666
683
  Uses ``ModuleRegistry`` for adaptive waiting:
@@ -668,28 +685,38 @@ async def _ensure_module_and_forward(method: str, params: Any, file_uri: str) ->
668
685
  - **UNKNOWN**: add module, wait until ready (adaptive, not fixed sleep)
669
686
  - **ADDED**: module sent but not confirmed — wait until ready
670
687
 
671
- When a request succeeds, marks the module as READY so subsequent
672
- requests skip the wait entirely.
688
+ When a request that requires full project indexing succeeds, marks the
689
+ module as READY so subsequent requests skip the wait entirely.
690
+ Methods in ``_LIGHTWEIGHT_METHODS`` operate on a single file and succeed
691
+ before cross-file indexing completes — their success is never used to
692
+ signal readiness.
673
693
  """
674
694
  proxy = server._proxy
675
695
  if not proxy.is_available:
676
696
  return None
677
697
 
678
698
  module_uri = _resolve_module_uri(file_uri)
699
+ short_mod = Path(to_fs_path(module_uri) or module_uri).name if module_uri else "<none>"
679
700
 
680
701
  # Hot path: module already confirmed working.
681
702
  if module_uri and proxy.modules.is_ready(module_uri):
682
- return await proxy.send_request(method, _serialize_params(params))
703
+ logger.info("jdtls: %s [hot] module=%s", method, short_mod)
704
+ result = await proxy.send_request(method, _serialize_params(params))
705
+ if result is None:
706
+ logger.info("jdtls: %s [hot] → null (jdtls has no result for this file_uri)", method)
707
+ return result
683
708
 
684
709
  # Cold path: add module if unknown, then wait for ready.
710
+ logger.info("jdtls: %s [cold] module=%s expanded=%s", method, short_mod, proxy._workspace_expanded)
685
711
  new_module_uri = await proxy.add_module_if_new(file_uri)
686
712
 
687
713
  serialized = _serialize_params(params)
688
714
  result = await proxy.send_request(method, serialized)
689
715
 
716
+ can_mark_ready = method not in _LIGHTWEIGHT_METHODS
690
717
  if result is not None:
691
- # Success — mark module as ready so future requests are instant.
692
- if module_uri:
718
+ # Success — mark module as ready so future cross-file requests are instant.
719
+ if can_mark_ready and module_uri:
693
720
  proxy.modules.mark_ready(module_uri)
694
721
  return result
695
722
 
@@ -698,11 +725,13 @@ async def _ensure_module_and_forward(method: str, params: Any, file_uri: str) ->
698
725
  # If a concurrent request succeeds, Event.set() wakes us early.
699
726
  wait_uri = new_module_uri or module_uri
700
727
  if wait_uri and not proxy.modules.is_ready(wait_uri):
728
+ logger.info("jdtls: %s [cold] waiting for module %s to become ready", method, short_mod)
701
729
  await proxy.modules.wait_until_ready(wait_uri, timeout=5.0)
702
730
  # Always retry once after waiting — even on timeout the module may be ready.
703
731
  result = await proxy.send_request(method, serialized)
704
- if result is not None and module_uri:
732
+ if result is not None and can_mark_ready and module_uri:
705
733
  proxy.modules.mark_ready(module_uri)
734
+ logger.info("jdtls: %s [cold] retry → %s", method, "result" if result is not None else "null")
706
735
  return result
707
736
 
708
737
 
@@ -782,8 +811,11 @@ async def _on_incoming_calls(
782
811
  if result is None:
783
812
  return None
784
813
  try:
785
- return [_converter.structure(c, lsp.CallHierarchyIncomingCall) for c in result]
786
- except Exception:
814
+ structured = [_converter.structure(c, lsp.CallHierarchyIncomingCall) for c in result]
815
+ logger.info("jdtls: incomingCalls → %d callers", len(structured))
816
+ return structured
817
+ except Exception as e:
818
+ logger.warning("jdtls: incomingCalls structuring failed: %s (raw=%r)", e, result)
787
819
  return None
788
820
 
789
821
 
@@ -581,6 +581,34 @@ class TestServerInternals:
581
581
  finally:
582
582
  srv._proxy.modules.clear()
583
583
 
584
+ async def test_ensure_module_and_forward_lightweight_methods_skip_transition(self) -> None:
585
+ """Methods in _LIGHTWEIGHT_METHODS do not transition module to READY."""
586
+ from unittest.mock import AsyncMock, patch
587
+
588
+ from java_functional_lsp.proxy import ModuleState
589
+ from java_functional_lsp.server import _LIGHTWEIGHT_METHODS, _ensure_module_and_forward
590
+ from java_functional_lsp.server import server as srv
591
+
592
+ mock_add = AsyncMock(return_value="file:///mod")
593
+ raw_result = [{"range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 3}}}]
594
+ mock_send = AsyncMock(return_value=raw_result)
595
+ try:
596
+ for lightweight_method in _LIGHTWEIGHT_METHODS:
597
+ srv._proxy.modules.clear()
598
+ with (
599
+ patch.object(srv._proxy, "add_module_if_new", mock_add),
600
+ patch.object(srv._proxy, "send_request", mock_send),
601
+ patch.object(srv._proxy, "_available", True),
602
+ patch("java_functional_lsp.server._resolve_module_uri", return_value="file:///mod"),
603
+ ):
604
+ await _ensure_module_and_forward(lightweight_method, {}, "file:///mod/F.java")
605
+ # Module should NOT be READY — lightweight op does not confirm full indexing.
606
+ assert srv._proxy.modules.get_state("file:///mod") != ModuleState.READY, (
607
+ f"{lightweight_method} should not mark module as READY"
608
+ )
609
+ finally:
610
+ srv._proxy.modules.clear()
611
+
584
612
  async def test_on_prepare_call_hierarchy_forwards_to_jdtls(self) -> None:
585
613
  """_on_prepare_call_hierarchy forwards request and structures result."""
586
614
  from unittest.mock import AsyncMock, patch