java-functional-lsp 0.9.15__tar.gz → 0.9.17__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.15 → java_functional_lsp-0.9.17}/PKG-INFO +1 -1
  2. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/intellij/README.md +1 -1
  3. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/pyproject.toml +1 -1
  4. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/__init__.py +1 -1
  5. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/cli.py +40 -25
  6. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_cli.py +43 -1
  7. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_server.py +7 -2
  8. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.claude-plugin/plugin.json +0 -0
  9. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.githooks/pre-commit +0 -0
  10. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.githooks/pre-push +0 -0
  11. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/CODEOWNERS +0 -0
  12. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
  13. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  14. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/SECURITY.md +0 -0
  16. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/dependabot.yml +0 -0
  17. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/release-drafter.yml +0 -0
  18. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/publish.yml +0 -0
  19. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/release-drafter.yml +0 -0
  20. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/stale.yml +0 -0
  21. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/test.yml +0 -0
  22. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/update-homebrew.yml +0 -0
  23. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.github/workflows/vscode-ext.yml +0 -0
  24. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/.gitignore +0 -0
  25. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/CONTRIBUTING.md +0 -0
  26. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/LICENSE +0 -0
  27. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/README.md +0 -0
  28. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/SKILL.md +0 -0
  29. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/commands/lint-java.md +0 -0
  30. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/intellij/lsp4ij-template.json +0 -0
  31. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/.vscodeignore +0 -0
  32. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/README.md +0 -0
  33. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/package-lock.json +0 -0
  34. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/package.json +0 -0
  35. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/src/extension.ts +0 -0
  36. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/editors/vscode/tsconfig.json +0 -0
  37. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/hooks/hooks.json +0 -0
  38. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/hooks/java_linter_reminder.py +0 -0
  39. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/scripts/ensure-lsp.sh +0 -0
  40. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/scripts/generate-formula.py +0 -0
  41. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/__main__.py +0 -0
  42. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/__init__.py +0 -0
  43. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/base.py +0 -0
  44. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
  45. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
  46. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
  47. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
  48. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
  49. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/fixes.py +0 -0
  50. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/merkle.py +0 -0
  51. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/proxy.py +0 -0
  52. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/src/java_functional_lsp/server.py +0 -0
  53. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/__init__.py +0 -0
  54. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/conftest.py +0 -0
  55. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_base.py +0 -0
  56. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_config.py +0 -0
  57. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_e2e.py +0 -0
  58. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_e2e_jdtls.py +0 -0
  59. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_exception_checker.py +0 -0
  60. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_fixes.py +0 -0
  61. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_functional_checker.py +0 -0
  62. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_merkle.py +0 -0
  63. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_merkle_proxy.py +0 -0
  64. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_mutation_checker.py +0 -0
  65. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_null_checker.py +0 -0
  66. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_proxy.py +0 -0
  67. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_spring_checker.py +0 -0
  68. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/tests/test_suppress.py +0 -0
  69. {java_functional_lsp-0.9.15 → java_functional_lsp-0.9.17}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-functional-lsp
3
- Version: 0.9.15
3
+ Version: 0.9.17
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
@@ -84,7 +84,7 @@ The server automatically detects JetBrains IDEs and disables the jdtls proxy —
84
84
  ### No diagnostics appear
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
- - Try restarting the language server: **Tools** → **Language Servers** → **Restart**
87
+ - Try restarting the language server: **Language Servers** (search it in intellij Actions [Double Shift on keyboard]) → click on java-functional-lsp → **Restart**
88
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`.
89
89
 
90
90
  ### PATH not found
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "java-functional-lsp"
7
- version = "0.9.15"
7
+ version = "0.9.17"
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.14"
3
+ __version__ = "0.9.17"
@@ -7,6 +7,7 @@ import sys
7
7
  from pathlib import Path
8
8
  from typing import Any
9
9
 
10
+ from . import __version__
10
11
  from .analyzers.base import Analyzer, Diagnostic, Severity, get_parser, is_excluded
11
12
  from .analyzers.exception_checker import ExceptionChecker
12
13
  from .analyzers.functional_checker import FunctionalChecker
@@ -66,14 +67,46 @@ def format_diagnostic(path: Path, d: Diagnostic) -> str:
66
67
  return f"{path}:{d.line + 1}:{d.col}: [{sym}] {d.code}: {d.message}"
67
68
 
68
69
 
70
+ _USAGE = """\
71
+ Usage: java-functional-lsp check <file.java> [file2.java ...]
72
+ java-functional-lsp check --dir <directory>
73
+ java-functional-lsp (start LSP server on stdio)
74
+ java-functional-lsp --version"""
75
+
76
+
77
+ def _collect_files(args: list[str]) -> list[Path]:
78
+ """Resolve CLI args to a list of .java paths."""
79
+ files: list[Path] = []
80
+ if args and args[0] == "--dir":
81
+ if len(args) < 2:
82
+ print("Error: --dir requires a directory path", file=sys.stderr)
83
+ sys.exit(1)
84
+ directory = Path(args[1])
85
+ if not directory.is_dir():
86
+ print(f"Error: {directory} is not a directory", file=sys.stderr)
87
+ sys.exit(1)
88
+ return sorted(directory.rglob("*.java"))
89
+ for arg in args:
90
+ p = Path(arg)
91
+ if p.is_file():
92
+ files.append(p)
93
+ elif p.is_dir():
94
+ files.extend(sorted(p.rglob("*.java")))
95
+ else:
96
+ print(f"Warning: {arg} not found, skipping", file=sys.stderr)
97
+ return files
98
+
99
+
69
100
  def main() -> None:
70
101
  """CLI entry point: java-functional-lsp check <files...>"""
71
102
  args = sys.argv[1:]
72
103
 
73
104
  if args and args[0] in ("-h", "--help"):
74
- print("Usage: java-functional-lsp check <file.java> [file2.java ...]")
75
- print(" java-functional-lsp check --dir <directory>")
76
- print(" java-functional-lsp (start LSP server on stdio)")
105
+ print(_USAGE)
106
+ sys.exit(0)
107
+
108
+ if args and args[0] in ("-v", "--version"):
109
+ print(f"java-functional-lsp {__version__}")
77
110
  sys.exit(0)
78
111
 
79
112
  if not args or args[0] != "check":
@@ -85,34 +118,16 @@ def main() -> None:
85
118
 
86
119
  args = args[1:] # skip "check"
87
120
 
88
- # Collect files
89
- files: list[Path] = []
90
- if args and args[0] == "--dir":
91
- if len(args) < 2:
92
- print("Error: --dir requires a directory path", file=sys.stderr)
93
- sys.exit(1)
94
- directory = Path(args[1])
95
- if not directory.is_dir():
96
- print(f"Error: {directory} is not a directory", file=sys.stderr)
97
- sys.exit(1)
98
- files = sorted(directory.rglob("*.java"))
99
- else:
100
- for arg in args:
101
- p = Path(arg)
102
- if p.is_file():
103
- files.append(p)
104
- elif p.is_dir():
105
- files.extend(sorted(p.rglob("*.java")))
106
- else:
107
- print(f"Warning: {arg} not found, skipping", file=sys.stderr)
121
+ if args and args[0] in ("-h", "--help"):
122
+ print(_USAGE)
123
+ sys.exit(0)
108
124
 
125
+ files = _collect_files(args)
109
126
  if not files:
110
127
  print("No .java files found", file=sys.stderr)
111
128
  sys.exit(1)
112
129
 
113
- # Load config from first file's directory
114
130
  config = load_config(files[0])
115
-
116
131
  total_diags = 0
117
132
  excludes: list[str] = config.get("excludes", [])
118
133
  for path in files:
@@ -4,8 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  from pathlib import Path
6
6
  from typing import Any
7
+ from unittest.mock import patch
7
8
 
8
- from java_functional_lsp.cli import check_file, format_diagnostic, load_config
9
+ import pytest
10
+
11
+ from java_functional_lsp.cli import check_file, format_diagnostic, load_config, main
9
12
 
10
13
 
11
14
  class TestCheckFile:
@@ -53,3 +56,42 @@ class TestLoadConfig:
53
56
  def test_missing_config(self, tmp_path: Path) -> None:
54
57
  config = load_config(tmp_path / "Test.java")
55
58
  assert config == {}
59
+
60
+
61
+ class TestHelp:
62
+ def test_top_level_help(self, capsys: pytest.CaptureFixture[str]) -> None:
63
+ with patch("sys.argv", ["java-functional-lsp", "--help"]):
64
+ with pytest.raises(SystemExit) as exc:
65
+ main()
66
+ assert exc.value.code == 0
67
+ assert "check" in capsys.readouterr().out
68
+
69
+ def test_check_subcommand_help(self, capsys: pytest.CaptureFixture[str]) -> None:
70
+ with patch("sys.argv", ["java-functional-lsp", "check", "--help"]):
71
+ with pytest.raises(SystemExit) as exc:
72
+ main()
73
+ assert exc.value.code == 0
74
+ assert "check" in capsys.readouterr().out
75
+
76
+ def test_check_short_help(self, capsys: pytest.CaptureFixture[str]) -> None:
77
+ with patch("sys.argv", ["java-functional-lsp", "check", "-h"]):
78
+ with pytest.raises(SystemExit) as exc:
79
+ main()
80
+ assert exc.value.code == 0
81
+ assert "check" in capsys.readouterr().out
82
+
83
+
84
+ class TestVersion:
85
+ def test_version_flag(self, capsys: pytest.CaptureFixture[str]) -> None:
86
+ with patch("sys.argv", ["java-functional-lsp", "--version"]):
87
+ with pytest.raises(SystemExit) as exc:
88
+ main()
89
+ assert exc.value.code == 0
90
+ assert "java-functional-lsp" in capsys.readouterr().out
91
+
92
+ def test_version_short_flag(self, capsys: pytest.CaptureFixture[str]) -> None:
93
+ with patch("sys.argv", ["java-functional-lsp", "-v"]):
94
+ with pytest.raises(SystemExit) as exc:
95
+ main()
96
+ assert exc.value.code == 0
97
+ assert "java-functional-lsp" in capsys.readouterr().out
@@ -109,12 +109,17 @@ async def lsp_client(tmp_path: Any) -> LanguageClient: # type: ignore[misc]
109
109
  try:
110
110
  yield client
111
111
  finally:
112
+ # Teardown with timeouts: the server subprocess may be mid-jdtls-startup
113
+ # on a slow CI runner, causing shutdown_async or stop to hang indefinitely.
112
114
  try:
113
- await client.shutdown_async(None)
115
+ await asyncio.wait_for(client.shutdown_async(None), timeout=5.0)
114
116
  client.exit(None)
115
117
  except Exception:
116
118
  pass
117
- await client.stop()
119
+ try:
120
+ await asyncio.wait_for(client.stop(), timeout=5.0)
121
+ except Exception:
122
+ pass
118
123
 
119
124
 
120
125
  async def _open_and_wait_for_diagnostics(