Skip to content

Commit 5cb6d5f

Browse files
authored
feat: optimize signature help interface (#11)
* feat: optimize signature help interface and helper methods * style: move import
1 parent 19d05bd commit 5cb6d5f

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

src/lsp_client/capability/request/__init__.py

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

0 commit comments

Comments
 (0)