java-functional-lsp 0.7.2__tar.gz → 0.7.3__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.2 → java_functional_lsp-0.7.3}/PKG-INFO +1 -1
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/pyproject.toml +1 -1
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/server.py +89 -16
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_server.py +136 -1
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/uv.lock +1 -1
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.claude-plugin/plugin.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.githooks/pre-commit +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.githooks/pre-push +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.gitignore +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/LICENSE +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/README.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/SKILL.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/package.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/__main__.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/base.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/exception_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/functional_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/mutation_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/cli.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/fixes.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/src/java_functional_lsp/proxy.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/__init__.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/conftest.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_base.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_config.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_e2e.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_e2e_jdtls.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_exception_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_fixes.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_functional_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_mutation_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/tests/test_suppress.py +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.3
|
|
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.3"
|
|
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" }
|
|
@@ -228,11 +228,11 @@ def on_initialize(params: lsp.InitializeParams) -> lsp.InitializeResult:
|
|
|
228
228
|
change=lsp.TextDocumentSyncKind.Full,
|
|
229
229
|
save=lsp.SaveOptions(include_text=True),
|
|
230
230
|
),
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
# Only advertise capabilities we own (custom diagnostics + code actions).
|
|
232
|
+
# jdtls-dependent features (hover, definition, references, completion,
|
|
233
|
+
# documentSymbol) are registered dynamically after jdtls starts — see
|
|
234
|
+
# on_initialized(). This prevents us from claiming hover when jdtls
|
|
235
|
+
# isn't ready, which would suppress the IDE's diagnostic tooltips.
|
|
236
236
|
code_action_provider=lsp.CodeActionOptions(
|
|
237
237
|
code_action_kinds=[lsp.CodeActionKind.QuickFix],
|
|
238
238
|
),
|
|
@@ -250,10 +250,71 @@ async def on_initialized(params: lsp.InitializedParams) -> None:
|
|
|
250
250
|
started = await server._proxy.start(server._init_params)
|
|
251
251
|
if started:
|
|
252
252
|
logger.info("jdtls proxy active — full Java language support enabled")
|
|
253
|
+
await _register_jdtls_capabilities()
|
|
253
254
|
else:
|
|
254
255
|
logger.info("jdtls proxy unavailable — running with custom rules only")
|
|
255
256
|
|
|
256
257
|
|
|
258
|
+
_JAVA_SELECTOR = [lsp.TextDocumentFilterLanguage(language="java")]
|
|
259
|
+
|
|
260
|
+
_JDTLS_REG_PREFIX = "jdtls-"
|
|
261
|
+
|
|
262
|
+
# jdtls-dependent capabilities registered dynamically after the proxy starts.
|
|
263
|
+
# Each entry: (id_suffix, LSP method, registration options class, extra kwargs).
|
|
264
|
+
_JDTLS_CAPABILITIES: list[tuple[str, str, type[Any], dict[str, Any]]] = [
|
|
265
|
+
("completion", lsp.TEXT_DOCUMENT_COMPLETION, lsp.CompletionRegistrationOptions, {"trigger_characters": ["."]}),
|
|
266
|
+
("hover", lsp.TEXT_DOCUMENT_HOVER, lsp.HoverRegistrationOptions, {}),
|
|
267
|
+
("definition", lsp.TEXT_DOCUMENT_DEFINITION, lsp.DefinitionRegistrationOptions, {}),
|
|
268
|
+
("references", lsp.TEXT_DOCUMENT_REFERENCES, lsp.ReferenceRegistrationOptions, {}),
|
|
269
|
+
("document-symbol", lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL, lsp.DocumentSymbolRegistrationOptions, {}),
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
# Maps LSP method → handler function for dynamic registration.
|
|
273
|
+
_JDTLS_HANDLERS: dict[str, Any] = {}
|
|
274
|
+
|
|
275
|
+
# Set after first successful registration to prevent FeatureAlreadyRegisteredError.
|
|
276
|
+
_jdtls_capabilities_registered = False
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _build_jdtls_registrations() -> list[lsp.Registration]:
|
|
280
|
+
"""Build LSP Registration objects for jdtls-dependent capabilities."""
|
|
281
|
+
return [
|
|
282
|
+
lsp.Registration(
|
|
283
|
+
id=f"{_JDTLS_REG_PREFIX}{suffix}",
|
|
284
|
+
method=method,
|
|
285
|
+
register_options=_converter.unstructure(opts_cls(document_selector=_JAVA_SELECTOR, **extra)),
|
|
286
|
+
)
|
|
287
|
+
for suffix, method, opts_cls, extra in _JDTLS_CAPABILITIES
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
async def _register_jdtls_capabilities() -> None:
|
|
292
|
+
"""Dynamically register jdtls-dependent capabilities after the proxy starts.
|
|
293
|
+
|
|
294
|
+
We don't advertise these in the static InitializeResult because doing so
|
|
295
|
+
would make the IDE defer hover/definition/etc to us even before jdtls is
|
|
296
|
+
ready, which suppresses the IDE's built-in diagnostic tooltips.
|
|
297
|
+
|
|
298
|
+
Idempotent: safe to call multiple times (e.g., proxy restart).
|
|
299
|
+
"""
|
|
300
|
+
global _jdtls_capabilities_registered
|
|
301
|
+
if _jdtls_capabilities_registered:
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# Register handlers so pygls dispatches incoming requests to them.
|
|
306
|
+
for method, handler in _JDTLS_HANDLERS.items():
|
|
307
|
+
server.feature(method)(handler)
|
|
308
|
+
|
|
309
|
+
# Tell the client we now support these capabilities.
|
|
310
|
+
registrations = _build_jdtls_registrations()
|
|
311
|
+
await server.client_register_capability_async(lsp.RegistrationParams(registrations=registrations))
|
|
312
|
+
_jdtls_capabilities_registered = True
|
|
313
|
+
logger.info("Dynamically registered jdtls capabilities (hover, definition, references, completion, symbol)")
|
|
314
|
+
except Exception:
|
|
315
|
+
logger.warning("Failed to dynamically register jdtls capabilities", exc_info=True)
|
|
316
|
+
|
|
317
|
+
|
|
257
318
|
# --- Document sync (forward to jdtls + run custom analyzers) ---
|
|
258
319
|
|
|
259
320
|
|
|
@@ -311,11 +372,15 @@ async def on_did_close(params: lsp.DidCloseTextDocumentParams) -> None:
|
|
|
311
372
|
await server._proxy.send_notification("textDocument/didClose", _serialize_params(params))
|
|
312
373
|
|
|
313
374
|
|
|
314
|
-
# ---
|
|
375
|
+
# --- jdtls passthrough handlers (registered dynamically, NOT at module level) ---
|
|
376
|
+
#
|
|
377
|
+
# These are NOT decorated with @server.feature because pygls auto-advertises
|
|
378
|
+
# capabilities for decorated handlers. Instead, they are collected in
|
|
379
|
+
# _JDTLS_HANDLERS and registered inside _register_jdtls_capabilities() so
|
|
380
|
+
# they only activate after jdtls starts.
|
|
315
381
|
|
|
316
382
|
|
|
317
|
-
|
|
318
|
-
async def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList | None:
|
|
383
|
+
async def _on_completion(params: lsp.CompletionParams) -> lsp.CompletionList | None:
|
|
319
384
|
"""Forward completion request to jdtls."""
|
|
320
385
|
if not server._proxy.is_available:
|
|
321
386
|
return None
|
|
@@ -328,8 +393,7 @@ async def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList | No
|
|
|
328
393
|
return None
|
|
329
394
|
|
|
330
395
|
|
|
331
|
-
|
|
332
|
-
async def on_hover(params: lsp.HoverParams) -> lsp.Hover | None:
|
|
396
|
+
async def _on_hover(params: lsp.HoverParams) -> lsp.Hover | None:
|
|
333
397
|
"""Forward hover request to jdtls."""
|
|
334
398
|
if not server._proxy.is_available:
|
|
335
399
|
return None
|
|
@@ -342,8 +406,7 @@ async def on_hover(params: lsp.HoverParams) -> lsp.Hover | None:
|
|
|
342
406
|
return None
|
|
343
407
|
|
|
344
408
|
|
|
345
|
-
|
|
346
|
-
async def on_definition(params: lsp.DefinitionParams) -> list[lsp.Location] | None:
|
|
409
|
+
async def _on_definition(params: lsp.DefinitionParams) -> list[lsp.Location] | None:
|
|
347
410
|
"""Forward go-to-definition request to jdtls."""
|
|
348
411
|
if not server._proxy.is_available:
|
|
349
412
|
return None
|
|
@@ -358,8 +421,7 @@ async def on_definition(params: lsp.DefinitionParams) -> list[lsp.Location] | No
|
|
|
358
421
|
return None
|
|
359
422
|
|
|
360
423
|
|
|
361
|
-
|
|
362
|
-
async def on_references(params: lsp.ReferenceParams) -> list[lsp.Location] | None:
|
|
424
|
+
async def _on_references(params: lsp.ReferenceParams) -> list[lsp.Location] | None:
|
|
363
425
|
"""Forward find-references request to jdtls."""
|
|
364
426
|
if not server._proxy.is_available:
|
|
365
427
|
return None
|
|
@@ -372,8 +434,7 @@ async def on_references(params: lsp.ReferenceParams) -> list[lsp.Location] | Non
|
|
|
372
434
|
return None
|
|
373
435
|
|
|
374
436
|
|
|
375
|
-
|
|
376
|
-
async def on_document_symbol(params: lsp.DocumentSymbolParams) -> list[lsp.DocumentSymbol] | None:
|
|
437
|
+
async def _on_document_symbol(params: lsp.DocumentSymbolParams) -> list[lsp.DocumentSymbol] | None:
|
|
377
438
|
"""Forward document symbol request to jdtls."""
|
|
378
439
|
if not server._proxy.is_available:
|
|
379
440
|
return None
|
|
@@ -386,6 +447,18 @@ async def on_document_symbol(params: lsp.DocumentSymbolParams) -> list[lsp.Docum
|
|
|
386
447
|
return None
|
|
387
448
|
|
|
388
449
|
|
|
450
|
+
# Populate handler map for dynamic registration.
|
|
451
|
+
_JDTLS_HANDLERS.update(
|
|
452
|
+
{
|
|
453
|
+
lsp.TEXT_DOCUMENT_COMPLETION: _on_completion,
|
|
454
|
+
lsp.TEXT_DOCUMENT_HOVER: _on_hover,
|
|
455
|
+
lsp.TEXT_DOCUMENT_DEFINITION: _on_definition,
|
|
456
|
+
lsp.TEXT_DOCUMENT_REFERENCES: _on_references,
|
|
457
|
+
lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL: _on_document_symbol,
|
|
458
|
+
}
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
389
462
|
# --- Code actions (quick fixes) ---
|
|
390
463
|
|
|
391
464
|
# Human-readable titles for code actions
|
|
@@ -290,6 +290,130 @@ class TestServerInternals:
|
|
|
290
290
|
finally:
|
|
291
291
|
server.workspace.remove_text_document(uri)
|
|
292
292
|
|
|
293
|
+
def test_init_capabilities_exclude_jdtls_features(self) -> None:
|
|
294
|
+
"""Static capabilities must NOT include hover/definition/references/completion/documentSymbol.
|
|
295
|
+
|
|
296
|
+
These are registered dynamically after jdtls starts, so the IDE doesn't
|
|
297
|
+
suppress diagnostic tooltips while jdtls is unavailable.
|
|
298
|
+
"""
|
|
299
|
+
from java_functional_lsp.server import on_initialize
|
|
300
|
+
|
|
301
|
+
result = on_initialize(
|
|
302
|
+
lsp.InitializeParams(
|
|
303
|
+
process_id=1,
|
|
304
|
+
root_uri="file:///tmp",
|
|
305
|
+
capabilities=lsp.ClientCapabilities(),
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
caps = result.capabilities
|
|
309
|
+
assert caps.code_action_provider is not None
|
|
310
|
+
assert caps.text_document_sync is not None
|
|
311
|
+
assert caps.hover_provider is None
|
|
312
|
+
assert caps.definition_provider is None
|
|
313
|
+
assert caps.references_provider is None
|
|
314
|
+
assert caps.completion_provider is None
|
|
315
|
+
assert caps.document_symbol_provider is None
|
|
316
|
+
|
|
317
|
+
def test_build_jdtls_registrations(self) -> None:
|
|
318
|
+
"""_build_jdtls_registrations returns one Registration per jdtls capability, each scoped to java files."""
|
|
319
|
+
from java_functional_lsp.server import _JDTLS_REG_PREFIX, _build_jdtls_registrations
|
|
320
|
+
|
|
321
|
+
regs = _build_jdtls_registrations()
|
|
322
|
+
assert len(regs) == 5
|
|
323
|
+
methods = {r.method for r in regs}
|
|
324
|
+
assert lsp.TEXT_DOCUMENT_HOVER in methods
|
|
325
|
+
assert lsp.TEXT_DOCUMENT_DEFINITION in methods
|
|
326
|
+
assert lsp.TEXT_DOCUMENT_REFERENCES in methods
|
|
327
|
+
assert lsp.TEXT_DOCUMENT_COMPLETION in methods
|
|
328
|
+
assert lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL in methods
|
|
329
|
+
# All IDs are unique and use the shared prefix
|
|
330
|
+
ids = {r.id for r in regs}
|
|
331
|
+
assert len(ids) == 5
|
|
332
|
+
assert all(rid.startswith(_JDTLS_REG_PREFIX) for rid in ids)
|
|
333
|
+
# All have java document selector with correct language
|
|
334
|
+
for r in regs:
|
|
335
|
+
assert r.register_options is not None
|
|
336
|
+
selectors = r.register_options["documentSelector"]
|
|
337
|
+
assert any(s.get("language") == "java" for s in selectors)
|
|
338
|
+
# Completion has triggerCharacters
|
|
339
|
+
comp = next(r for r in regs if r.method == lsp.TEXT_DOCUMENT_COMPLETION)
|
|
340
|
+
assert comp.register_options.get("triggerCharacters") == ["."]
|
|
341
|
+
|
|
342
|
+
async def test_register_jdtls_capabilities_logs_on_failure(self, caplog: Any) -> None:
|
|
343
|
+
"""_register_jdtls_capabilities logs a warning when the client rejects."""
|
|
344
|
+
import logging
|
|
345
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
346
|
+
|
|
347
|
+
import java_functional_lsp.server as srv_mod
|
|
348
|
+
from java_functional_lsp.server import server as srv
|
|
349
|
+
|
|
350
|
+
# Patch both server.feature (to avoid FeatureAlreadyRegisteredError on
|
|
351
|
+
# the shared singleton) and client_register_capability_async (to trigger error).
|
|
352
|
+
mock_reg = AsyncMock(side_effect=Exception("no"))
|
|
353
|
+
mock_feature = MagicMock(return_value=lambda fn: fn)
|
|
354
|
+
old_flag = srv_mod._jdtls_capabilities_registered
|
|
355
|
+
srv_mod._jdtls_capabilities_registered = False
|
|
356
|
+
try:
|
|
357
|
+
with (
|
|
358
|
+
caplog.at_level(logging.WARNING, logger="java_functional_lsp.server"),
|
|
359
|
+
patch.object(srv, "client_register_capability_async", mock_reg),
|
|
360
|
+
patch.object(srv, "feature", mock_feature),
|
|
361
|
+
):
|
|
362
|
+
await srv_mod._register_jdtls_capabilities()
|
|
363
|
+
finally:
|
|
364
|
+
srv_mod._jdtls_capabilities_registered = old_flag
|
|
365
|
+
assert any("Failed to dynamically register" in r.getMessage() for r in caplog.records)
|
|
366
|
+
|
|
367
|
+
async def test_register_jdtls_capabilities_happy_path(self, caplog: Any) -> None:
|
|
368
|
+
"""On success, handlers are registered and info log is emitted."""
|
|
369
|
+
import logging
|
|
370
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
371
|
+
|
|
372
|
+
import java_functional_lsp.server as srv_mod
|
|
373
|
+
from java_functional_lsp.server import server as srv
|
|
374
|
+
|
|
375
|
+
mock_reg = AsyncMock(return_value=None)
|
|
376
|
+
registered_methods: list[str] = []
|
|
377
|
+
mock_feature = MagicMock(side_effect=lambda m: registered_methods.append(m) or (lambda fn: fn))
|
|
378
|
+
old_flag = srv_mod._jdtls_capabilities_registered
|
|
379
|
+
srv_mod._jdtls_capabilities_registered = False
|
|
380
|
+
try:
|
|
381
|
+
with (
|
|
382
|
+
caplog.at_level(logging.INFO, logger="java_functional_lsp.server"),
|
|
383
|
+
patch.object(srv, "client_register_capability_async", mock_reg),
|
|
384
|
+
patch.object(srv, "feature", mock_feature),
|
|
385
|
+
):
|
|
386
|
+
await srv_mod._register_jdtls_capabilities()
|
|
387
|
+
finally:
|
|
388
|
+
srv_mod._jdtls_capabilities_registered = old_flag
|
|
389
|
+
# Handlers were registered for all 5 methods
|
|
390
|
+
assert lsp.TEXT_DOCUMENT_HOVER in registered_methods
|
|
391
|
+
assert lsp.TEXT_DOCUMENT_COMPLETION in registered_methods
|
|
392
|
+
assert lsp.TEXT_DOCUMENT_DEFINITION in registered_methods
|
|
393
|
+
assert lsp.TEXT_DOCUMENT_REFERENCES in registered_methods
|
|
394
|
+
assert lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL in registered_methods
|
|
395
|
+
# client_register_capability_async was called
|
|
396
|
+
mock_reg.assert_called_once()
|
|
397
|
+
# Success log emitted
|
|
398
|
+
assert any("Dynamically registered" in r.getMessage() for r in caplog.records)
|
|
399
|
+
|
|
400
|
+
async def test_register_jdtls_capabilities_idempotent(self) -> None:
|
|
401
|
+
"""Second call is a no-op (idempotency guard)."""
|
|
402
|
+
from unittest.mock import AsyncMock, patch
|
|
403
|
+
|
|
404
|
+
import java_functional_lsp.server as srv_mod
|
|
405
|
+
from java_functional_lsp.server import server as srv
|
|
406
|
+
|
|
407
|
+
mock_reg = AsyncMock()
|
|
408
|
+
old_flag = srv_mod._jdtls_capabilities_registered
|
|
409
|
+
srv_mod._jdtls_capabilities_registered = True
|
|
410
|
+
try:
|
|
411
|
+
with patch.object(srv, "client_register_capability_async", mock_reg):
|
|
412
|
+
await srv_mod._register_jdtls_capabilities()
|
|
413
|
+
finally:
|
|
414
|
+
srv_mod._jdtls_capabilities_registered = old_flag
|
|
415
|
+
mock_reg.assert_not_called()
|
|
416
|
+
|
|
293
417
|
def test_serialize_params_camelcase(self) -> None:
|
|
294
418
|
from java_functional_lsp.server import _serialize_params
|
|
295
419
|
|
|
@@ -396,11 +520,22 @@ class TestLspLifecycle:
|
|
|
396
520
|
"""Full LSP lifecycle tests via real stdio transport — zero mocks."""
|
|
397
521
|
|
|
398
522
|
async def test_initialize_reports_capabilities(self, lsp_client: LanguageClient) -> None:
|
|
399
|
-
"""Server advertises codeActionProvider and textDocumentSync.
|
|
523
|
+
"""Server advertises codeActionProvider and textDocumentSync but NOT jdtls features.
|
|
524
|
+
|
|
525
|
+
jdtls-dependent capabilities (hover, definition, references, completion,
|
|
526
|
+
documentSymbol) are registered dynamically after jdtls starts, so they
|
|
527
|
+
should NOT appear in the static InitializeResult.
|
|
528
|
+
"""
|
|
400
529
|
caps = lsp_client._server_capabilities # type: ignore[attr-defined]
|
|
401
530
|
assert caps is not None
|
|
402
531
|
assert caps.code_action_provider is not None
|
|
403
532
|
assert caps.text_document_sync is not None
|
|
533
|
+
# jdtls features are NOT statically advertised (registered dynamically)
|
|
534
|
+
assert caps.hover_provider is None
|
|
535
|
+
assert caps.definition_provider is None
|
|
536
|
+
assert caps.references_provider is None
|
|
537
|
+
assert caps.completion_provider is None
|
|
538
|
+
assert caps.document_symbol_provider is None
|
|
404
539
|
|
|
405
540
|
async def test_null_return_diagnostic_published(self, lsp_client: LanguageClient) -> None:
|
|
406
541
|
"""didOpen a file with ``return null`` → server publishes null-return diagnostic."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.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.2 → java_functional_lsp-0.7.3}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.7.2 → java_functional_lsp-0.7.3}/.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.2 → java_functional_lsp-0.7.3}/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.2 → java_functional_lsp-0.7.3}/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
|