Skip to content

Commit 19d05bd

Browse files
authored
feat: add support for textDocument/completion capability (#10)
* feat: add support for textDocument/completion capability * doc: update class doc
1 parent 30ec5b1 commit 19d05bd

File tree

9 files changed

+142
-0
lines changed

9 files changed

+142
-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
@@ -17,6 +18,7 @@
1718

1819
capabilities: Final = (
1920
WithRequestCallHierarchy,
21+
WithRequestCompletion,
2022
WithRequestDeclaration,
2123
WithRequestDefinition,
2224
WithRequestDocumentSymbol,
@@ -32,6 +34,7 @@
3234

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

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,
@@ -82,6 +83,7 @@ async def ensure_deno_installed() -> None:
8283
class DenoClient(
8384
Client,
8485
WithRequestHover,
86+
WithRequestCompletion,
8587
WithRequestDefinition,
8688
WithRequestReferences,
8789
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,
@@ -80,6 +81,7 @@ class PyreflyClient(
8081
Client,
8182
WithNotifyDidChangeConfiguration,
8283
WithRequestCallHierarchy,
84+
WithRequestCompletion,
8385
WithRequestDeclaration,
8486
WithRequestDefinition,
8587
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,
@@ -76,6 +77,7 @@ class PyrightClient(
7677
Client,
7778
WithNotifyDidChangeConfiguration,
7879
WithRequestCallHierarchy,
80+
WithRequestCompletion,
7981
WithRequestDeclaration,
8082
WithRequestDefinition,
8183
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,
@@ -76,6 +77,7 @@ class RustAnalyzerClient(
7677
Client,
7778
WithNotifyDidChangeConfiguration,
7879
WithRequestCallHierarchy,
80+
WithRequestCompletion,
7981
WithRequestDeclaration,
8082
WithRequestDefinition,
8183
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,
@@ -73,6 +74,7 @@ async def ensure_ty_installed() -> None:
7374
class TyClient(
7475
Client,
7576
WithNotifyDidChangeConfiguration,
77+
WithRequestCompletion,
7678
WithRequestDeclaration,
7779
WithRequestDefinition,
7880
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,
@@ -80,6 +81,7 @@ async def ensure_typescript_installed() -> None:
8081
class TypescriptClient(
8182
Client,
8283
WithNotifyDidChangeConfiguration,
84+
WithRequestCompletion,
8385
WithRequestHover,
8486
WithRequestDefinition,
8587
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)