Skip to content

Commit ce25091

Browse files
committed
feat: add support for textDocument/completion capability
1 parent 38bb3d2 commit ce25091

File tree

9 files changed

+141
-0
lines changed

9 files changed

+141
-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 .completion import WithRequestCompletion
67
from .declaration import WithRequestDeclaration
78
from .definition import WithRequestDefinition
89
from .document_symbol import WithRequestDocumentSymbol
@@ -16,6 +17,7 @@
1617

1718
capabilities: Final = (
1819
WithRequestCallHierarchy,
20+
WithRequestCompletion,
1921
WithRequestDeclaration,
2022
WithRequestDefinition,
2123
WithRequestDocumentSymbol,
@@ -30,6 +32,7 @@
3032

3133
__all__ = [
3234
"WithRequestCallHierarchy",
35+
"WithRequestCompletion",
3336
"WithRequestDeclaration",
3437
"WithRequestDefinition",
3538
"WithRequestDocumentSymbol",
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Sequence
4+
from typing import Protocol, override, runtime_checkable
5+
6+
import asyncer
7+
8+
from lsp_client.jsonrpc.id import jsonrpc_uuid
9+
from lsp_client.protocol import (
10+
CapabilityClientProtocol,
11+
TextDocumentCapabilityProtocol,
12+
)
13+
from lsp_client.utils.type_guard import is_completion_items
14+
from lsp_client.utils.types import AnyPath, Position, lsp_type
15+
16+
17+
@runtime_checkable
18+
class WithRequestCompletion(
19+
TextDocumentCapabilityProtocol,
20+
CapabilityClientProtocol,
21+
Protocol,
22+
):
23+
"""
24+
`textDocument/completion` - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
25+
"""
26+
27+
@override
28+
@classmethod
29+
def methods(cls) -> Sequence[str]:
30+
return (
31+
lsp_type.TEXT_DOCUMENT_COMPLETION,
32+
lsp_type.COMPLETION_ITEM_RESOLVE,
33+
)
34+
35+
@override
36+
@classmethod
37+
def register_text_document_capability(
38+
cls, cap: lsp_type.TextDocumentClientCapabilities
39+
) -> None:
40+
cap.completion = lsp_type.CompletionClientCapabilities(
41+
completion_item=lsp_type.ClientCompletionItemOptions(
42+
snippet_support=True,
43+
resolve_support=lsp_type.ClientCompletionItemResolveOptions(
44+
properties=["documentation", "additionalTextEdits"]
45+
),
46+
),
47+
context_support=True,
48+
)
49+
50+
@override
51+
@classmethod
52+
def check_server_capability(
53+
cls,
54+
cap: lsp_type.ServerCapabilities,
55+
info: lsp_type.ServerInfo | None,
56+
) -> None:
57+
super().check_server_capability(cap, info)
58+
assert cap.completion_provider
59+
60+
async def request_completion(
61+
self,
62+
file_path: AnyPath,
63+
position: Position,
64+
*,
65+
trigger_character: str | None = None,
66+
trigger_kind: lsp_type.CompletionTriggerKind = lsp_type.CompletionTriggerKind.Invoked,
67+
resolve: bool = False,
68+
) -> Sequence[lsp_type.CompletionItem]:
69+
context = lsp_type.CompletionContext(
70+
trigger_kind=trigger_kind,
71+
trigger_character=trigger_character,
72+
)
73+
result = await self.file_request(
74+
lsp_type.CompletionRequest(
75+
id=jsonrpc_uuid(),
76+
params=lsp_type.CompletionParams(
77+
text_document=lsp_type.TextDocumentIdentifier(
78+
uri=self.as_uri(file_path)
79+
),
80+
position=position,
81+
context=context,
82+
),
83+
),
84+
schema=lsp_type.CompletionResponse,
85+
file_paths=[file_path],
86+
)
87+
88+
match result:
89+
case lsp_type.CompletionList(items=items):
90+
res = list(items)
91+
case items if is_completion_items(items):
92+
res = list(items)
93+
case _:
94+
res = []
95+
96+
if resolve and res:
97+
return await self.resolve_completion_items(res)
98+
return res
99+
100+
async def resolve_completion_items(
101+
self,
102+
items: Sequence[lsp_type.CompletionItem],
103+
) -> Sequence[lsp_type.CompletionItem]:
104+
async with asyncer.create_task_group() as tg:
105+
tasks = [
106+
tg.soonify(self.request_completion_resolve)(item) for item in items
107+
]
108+
return [task.value for task in tasks]
109+
110+
async def request_completion_resolve(
111+
self,
112+
item: lsp_type.CompletionItem,
113+
) -> lsp_type.CompletionItem:
114+
return await self.request(
115+
lsp_type.CompletionResolveRequest(
116+
id=jsonrpc_uuid(),
117+
params=item,
118+
),
119+
schema=lsp_type.CompletionResolveResponse,
120+
)

src/lsp_client/clients/deno/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from lsp_client.capability.request import (
1313
WithRequestCallHierarchy,
14+
WithRequestCompletion,
1415
WithRequestDefinition,
1516
WithRequestDocumentSymbol,
1617
WithRequestHover,
@@ -81,6 +82,7 @@ async def ensure_deno_installed() -> None:
8182
class DenoClient(
8283
Client,
8384
WithRequestHover,
85+
WithRequestCompletion,
8486
WithRequestDefinition,
8587
WithRequestReferences,
8688
WithRequestImplementation,

src/lsp_client/clients/pyrefly.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
)
1515
from lsp_client.capability.request import (
1616
WithRequestCallHierarchy,
17+
WithRequestCompletion,
1718
WithRequestDeclaration,
1819
WithRequestDefinition,
1920
WithRequestDocumentSymbol,
@@ -79,6 +80,7 @@ class PyreflyClient(
7980
Client,
8081
WithNotifyDidChangeConfiguration,
8182
WithRequestCallHierarchy,
83+
WithRequestCompletion,
8284
WithRequestDeclaration,
8385
WithRequestDefinition,
8486
WithRequestDocumentSymbol,

src/lsp_client/clients/pyright.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
)
1515
from lsp_client.capability.request import (
1616
WithRequestCallHierarchy,
17+
WithRequestCompletion,
1718
WithRequestDeclaration,
1819
WithRequestDefinition,
1920
WithRequestDocumentSymbol,
@@ -75,6 +76,7 @@ class PyrightClient(
7576
Client,
7677
WithNotifyDidChangeConfiguration,
7778
WithRequestCallHierarchy,
79+
WithRequestCompletion,
7880
WithRequestDeclaration,
7981
WithRequestDefinition,
8082
WithRequestDocumentSymbol,

src/lsp_client/clients/rust_analyzer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
)
1515
from lsp_client.capability.request import (
1616
WithRequestCallHierarchy,
17+
WithRequestCompletion,
1718
WithRequestDeclaration,
1819
WithRequestDefinition,
1920
WithRequestDocumentSymbol,
@@ -75,6 +76,7 @@ class RustAnalyzerClient(
7576
Client,
7677
WithNotifyDidChangeConfiguration,
7778
WithRequestCallHierarchy,
79+
WithRequestCompletion,
7880
WithRequestDeclaration,
7981
WithRequestDefinition,
8082
WithRequestDocumentSymbol,

src/lsp_client/clients/ty.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
WithNotifyDidChangeConfiguration,
1515
)
1616
from lsp_client.capability.request import (
17+
WithRequestCompletion,
1718
WithRequestDeclaration,
1819
WithRequestDefinition,
1920
WithRequestDocumentSymbol,
@@ -72,6 +73,7 @@ async def ensure_ty_installed() -> None:
7273
class TyClient(
7374
Client,
7475
WithNotifyDidChangeConfiguration,
76+
WithRequestCompletion,
7577
WithRequestDeclaration,
7678
WithRequestDefinition,
7779
WithRequestDocumentSymbol,

src/lsp_client/clients/typescript.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
WithNotifyDidChangeConfiguration,
1414
)
1515
from lsp_client.capability.request import (
16+
WithRequestCompletion,
1617
WithRequestDefinition,
1718
WithRequestDocumentSymbol,
1819
WithRequestHover,
@@ -79,6 +80,7 @@ async def ensure_typescript_installed() -> None:
7980
class TypescriptClient(
8081
Client,
8182
WithNotifyDidChangeConfiguration,
83+
WithRequestCompletion,
8284
WithRequestHover,
8385
WithRequestDefinition,
8486
WithRequestReferences,

src/lsp_client/utils/type_guard.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,9 @@ def is_symbol_information_seq(
4242
return result is not None and all(
4343
isinstance(item, lsp_type.SymbolInformation) for item in result
4444
)
45+
46+
47+
def is_completion_items(result: Any) -> TypeGuard[Iterable[lsp_type.CompletionItem]]:
48+
return result is not None and all(
49+
isinstance(item, lsp_type.CompletionItem) for item in result
50+
)

0 commit comments

Comments
 (0)