Skip to content

Commit fb1c239

Browse files
committed
feat: add support for textDocument/codeAction
1 parent fb27e07 commit fb1c239

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

src/lsp_client/capability/request/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Final
44

55
from .call_hierarchy import WithRequestCallHierarchy
6+
from .code_action import WithRequestCodeAction
67
from .completion import WithRequestCompletion
78
from .declaration import WithRequestDeclaration
89
from .definition import WithRequestDefinition
@@ -20,6 +21,7 @@
2021

2122
capabilities: Final = (
2223
WithRequestCallHierarchy,
24+
WithRequestCodeAction,
2325
WithRequestCompletion,
2426
WithRequestDeclaration,
2527
WithRequestDefinition,
@@ -38,6 +40,7 @@
3840

3941
__all__ = [
4042
"WithRequestCallHierarchy",
43+
"WithRequestCodeAction",
4144
"WithRequestCompletion",
4245
"WithRequestDeclaration",
4346
"WithRequestDefinition",
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Iterator, Sequence
4+
from typing import Protocol, override, runtime_checkable
5+
6+
from lsp_client.jsonrpc.id import jsonrpc_uuid
7+
from lsp_client.protocol import CapabilityClientProtocol, TextDocumentCapabilityProtocol
8+
from lsp_client.utils.types import AnyPath, Range, lsp_type
9+
10+
11+
@runtime_checkable
12+
class WithRequestCodeAction(
13+
TextDocumentCapabilityProtocol,
14+
CapabilityClientProtocol,
15+
Protocol,
16+
):
17+
"""
18+
- `textDocument/codeAction` - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction
19+
- `codeAction/resolve` - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeAction_resolve
20+
"""
21+
22+
@override
23+
@classmethod
24+
def iter_methods(cls) -> Iterator[str]:
25+
yield from super().iter_methods()
26+
yield from (
27+
lsp_type.TEXT_DOCUMENT_CODE_ACTION,
28+
lsp_type.CODE_ACTION_RESOLVE,
29+
)
30+
31+
@override
32+
@classmethod
33+
def register_text_document_capability(
34+
cls, cap: lsp_type.TextDocumentClientCapabilities
35+
) -> None:
36+
cap.code_action = lsp_type.CodeActionClientCapabilities(
37+
code_action_literal_support=lsp_type.ClientCodeActionLiteralOptions(
38+
code_action_kind=lsp_type.ClientCodeActionKindOptions(
39+
value_set=list(lsp_type.CodeActionKind)
40+
)
41+
),
42+
is_preferred_support=True,
43+
disabled_support=True,
44+
data_support=True,
45+
resolve_support=lsp_type.ClientCodeActionResolveOptions(
46+
properties=["edit", "command"]
47+
),
48+
)
49+
50+
@override
51+
@classmethod
52+
def check_server_capability(cls, cap: lsp_type.ServerCapabilities) -> None:
53+
super().check_server_capability(cap)
54+
assert cap.code_action_provider
55+
56+
async def _request_code_action(
57+
self, params: lsp_type.CodeActionParams
58+
) -> lsp_type.CodeActionResult:
59+
return await self.request(
60+
lsp_type.CodeActionRequest(
61+
id=jsonrpc_uuid(),
62+
params=params,
63+
),
64+
schema=lsp_type.CodeActionResponse,
65+
)
66+
67+
async def _request_code_action_resolve(
68+
self, params: lsp_type.CodeAction
69+
) -> lsp_type.CodeAction:
70+
return await self.request(
71+
lsp_type.CodeActionResolveRequest(
72+
id=jsonrpc_uuid(),
73+
params=params,
74+
),
75+
schema=lsp_type.CodeActionResolveResponse,
76+
)
77+
78+
async def request_code_action(
79+
self,
80+
file_path: AnyPath,
81+
range: Range,
82+
*,
83+
diagnostics: Sequence[lsp_type.Diagnostic] | None = None,
84+
only: Sequence[lsp_type.CodeActionKind] | None = None,
85+
trigger_kind: lsp_type.CodeActionTriggerKind | None = None,
86+
) -> Sequence[lsp_type.Command | lsp_type.CodeAction]:
87+
context = lsp_type.CodeActionContext(
88+
diagnostics=list(diagnostics) if diagnostics else [],
89+
only=list(only) if only else None,
90+
trigger_kind=trigger_kind,
91+
)
92+
93+
async with self.open_files(file_path):
94+
result = await self._request_code_action(
95+
lsp_type.CodeActionParams(
96+
text_document=lsp_type.TextDocumentIdentifier(
97+
uri=self.as_uri(file_path)
98+
),
99+
range=range,
100+
context=context,
101+
)
102+
)
103+
104+
return list(result) if result else []
105+
106+
async def request_code_action_resolve(
107+
self,
108+
code_action: lsp_type.CodeAction,
109+
) -> lsp_type.CodeAction:
110+
return await self._request_code_action_resolve(code_action)

src/lsp_client/utils/type_guard.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,11 @@ def is_completion_items(result: Any) -> TypeGuard[Iterable[lsp_type.CompletionIt
4848
return result is not None and all(
4949
isinstance(item, lsp_type.CompletionItem) for item in result
5050
)
51+
52+
53+
def is_code_actions(
54+
result: Any,
55+
) -> TypeGuard[Iterable[lsp_type.Command | lsp_type.CodeAction]]:
56+
return result is not None and all(
57+
isinstance(item, (lsp_type.Command, lsp_type.CodeAction)) for item in result
58+
)

0 commit comments

Comments
 (0)