Skip to content

Commit 6c9e029

Browse files
committed
feat: optimize signature help interface and helper methods
1 parent 38bb3d2 commit 6c9e029

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

src/lsp_client/capability/request/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .implementation import WithRequestImplementation
1111
from .inline_value import WithRequestInlineValue
1212
from .reference import WithRequestReferences
13+
from .signature_help import WithRequestSignatureHelp
1314
from .type_definition import WithRequestTypeDefinition
1415
from .type_hierarchy import WithRequestTypeHierarchy
1516
from .workspace_symbol import WithRequestWorkspaceSymbol
@@ -23,6 +24,7 @@
2324
WithRequestImplementation,
2425
WithRequestInlineValue,
2526
WithRequestReferences,
27+
WithRequestSignatureHelp,
2628
WithRequestTypeDefinition,
2729
WithRequestTypeHierarchy,
2830
WithRequestWorkspaceSymbol,
@@ -37,6 +39,7 @@
3739
"WithRequestImplementation",
3840
"WithRequestInlineValue",
3941
"WithRequestReferences",
42+
"WithRequestSignatureHelp",
4043
"WithRequestTypeDefinition",
4144
"WithRequestTypeHierarchy",
4245
"WithRequestWorkspaceSymbol",
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import 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, Position, lsp_type
9+
10+
11+
@runtime_checkable
12+
class WithRequestSignatureHelp(
13+
TextDocumentCapabilityProtocol,
14+
CapabilityClientProtocol,
15+
Protocol,
16+
):
17+
"""
18+
`textDocument/signatureHelp` - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_signatureHelp
19+
"""
20+
21+
@override
22+
@classmethod
23+
def methods(cls) -> Sequence[str]:
24+
return (lsp_type.TEXT_DOCUMENT_SIGNATURE_HELP,)
25+
26+
@override
27+
@classmethod
28+
def register_text_document_capability(
29+
cls, cap: lsp_type.TextDocumentClientCapabilities
30+
) -> None:
31+
cap.signature_help = lsp_type.SignatureHelpClientCapabilities(
32+
context_support=True,
33+
signature_information=lsp_type.ClientSignatureInformationOptions(
34+
documentation_format=[
35+
lsp_type.MarkupKind.Markdown,
36+
lsp_type.MarkupKind.PlainText,
37+
],
38+
parameter_information=lsp_type.ClientSignatureParameterInformationOptions(
39+
label_offset_support=True,
40+
),
41+
active_parameter_support=True,
42+
),
43+
)
44+
45+
@override
46+
@classmethod
47+
def check_server_capability(
48+
cls,
49+
cap: lsp_type.ServerCapabilities,
50+
info: lsp_type.ServerInfo | None,
51+
) -> None:
52+
super().check_server_capability(cap, info)
53+
assert cap.signature_help_provider
54+
55+
async def request_signature_help(
56+
self,
57+
file_path: AnyPath,
58+
position: Position,
59+
*,
60+
trigger_character: str | None = None,
61+
is_retrigger: bool | None = None,
62+
active_signature_help: lsp_type.SignatureHelp | None = None,
63+
trigger_kind: lsp_type.SignatureHelpTriggerKind | None = None,
64+
) -> lsp_type.SignatureHelp | None:
65+
if is_retrigger is None:
66+
is_retrigger = active_signature_help is not None
67+
68+
if trigger_kind is None:
69+
trigger_kind = (
70+
lsp_type.SignatureHelpTriggerKind.TriggerCharacter
71+
if trigger_character is not None
72+
else lsp_type.SignatureHelpTriggerKind.Invoked
73+
)
74+
75+
context = lsp_type.SignatureHelpContext(
76+
trigger_kind=trigger_kind,
77+
trigger_character=trigger_character,
78+
is_retrigger=is_retrigger,
79+
active_signature_help=active_signature_help,
80+
)
81+
82+
return await self.file_request(
83+
lsp_type.SignatureHelpRequest(
84+
id=jsonrpc_uuid(),
85+
params=lsp_type.SignatureHelpParams(
86+
text_document=lsp_type.TextDocumentIdentifier(
87+
uri=self.as_uri(file_path)
88+
),
89+
position=position,
90+
context=context,
91+
),
92+
),
93+
schema=lsp_type.SignatureHelpResponse,
94+
file_paths=[file_path],
95+
)
96+
97+
async def request_active_signature(
98+
self,
99+
file_path: AnyPath,
100+
position: Position,
101+
*,
102+
trigger_character: str | None = None,
103+
active_signature_help: lsp_type.SignatureHelp | None = None,
104+
) -> lsp_type.SignatureInformation | None:
105+
"""
106+
Request the signature help and return the currently active signature.
107+
108+
The `active_parameter` of the returned signature is automatically resolved:
109+
it prefers the `active_parameter` from the top-level `SignatureHelp` response
110+
over the one defined in `SignatureInformation`, following the LSP specification.
111+
"""
112+
import attrs
113+
114+
res = await self.request_signature_help(
115+
file_path,
116+
position,
117+
trigger_character=trigger_character,
118+
active_signature_help=active_signature_help,
119+
)
120+
if not res or not res.signatures:
121+
return None
122+
123+
sig_idx = res.active_signature if res.active_signature is not None else 0
124+
if not (0 <= sig_idx < len(res.signatures)):
125+
sig_idx = 0
126+
127+
sig = res.signatures[sig_idx]
128+
active_param = (
129+
res.active_parameter
130+
if res.active_parameter is not None
131+
else sig.active_parameter
132+
)
133+
134+
return attrs.evolve(sig, active_parameter=active_param)

0 commit comments

Comments
 (0)