Skip to content

Commit f4c1d83

Browse files
committed
implement inlay hints for parameters and namespaces
1 parent 901c3c2 commit f4c1d83

File tree

11 files changed

+1199
-775
lines changed

11 files changed

+1199
-775
lines changed

CHANGELOG.md

Lines changed: 771 additions & 767 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@
7575
"configurationDefaults": {
7676
"[robotframework]": {
7777
"editor.wordBasedSuggestions": false,
78-
"editor.semanticHighlighting.enabled": true
78+
"editor.semanticHighlighting.enabled": true,
79+
"editor.inlayHints.enabled": "offUnlessPressed"
7980
}
8081
},
8182
"semanticTokenModifiers": [
@@ -559,6 +560,18 @@
559560
"default": null,
560561
"markdownDescription": "Defines the header style format. If not defined ```*** {name} ***``` is used.",
561562
"scope": "resource"
563+
},
564+
"robotcode.inlayHints.parameterNames": {
565+
"type": "boolean",
566+
"default": true,
567+
"markdownDescription": "Enable/disable inlay hints for parameter names.",
568+
"scope": "resource"
569+
},
570+
"robotcode.inlayHints.namespaces": {
571+
"type": "boolean",
572+
"default": true,
573+
"markdownDescription": "Enable/disable inlay hints for namespaces.",
574+
"scope": "resource"
562575
}
563576
}
564577
}

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ classifiers = [
3939
"Programming Language :: Python :: 3.8",
4040
"Programming Language :: Python :: 3.9",
4141
"Programming Language :: Python :: 3.10",
42+
"Programming Language :: Python :: 3.11",
4243
"Programming Language :: Python :: Implementation",
4344
"Topic :: Software Development :: Testing",
4445
"Topic :: Software Development :: Testing :: Acceptance",

robotcode/language_server/common/lsp_types.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ def __repr__(self) -> str: # pragma: no cover
204204

205205

206206
class CompletionItemTag(IntEnum):
207-
Deprecated = 1
207+
DEPRECATED = 1
208208

209209
def __repr__(self) -> str: # pragma: no cover
210210
return super().__str__()
211211

212212

213213
class SymbolTag(IntEnum):
214-
Deprecated = 1
214+
DEPRECATED = 1
215215

216216
def __repr__(self) -> str: # pragma: no cover
217217
return super().__str__()
@@ -1127,6 +1127,21 @@ class CodeActionOptions(WorkDoneProgressOptions, _CodeActionOptions):
11271127
pass
11281128

11291129

1130+
@dataclass(repr=False)
1131+
class _InlayHintOptions(Model):
1132+
resolve_provider: Optional[bool] = None
1133+
1134+
1135+
@dataclass(repr=False)
1136+
class InlayHintOptions(WorkDoneProgressOptions, _InlayHintOptions):
1137+
pass
1138+
1139+
1140+
@dataclass(repr=False)
1141+
class InlayHintRegistrationOptions(InlayHintOptions, TextDocumentRegistrationOptions, StaticRegistrationOptions):
1142+
pass
1143+
1144+
11301145
@dataclass(repr=False)
11311146
class ServerCapabilities(Model):
11321147
position_encoding: Optional[PositionEncodingKind] = None
@@ -1160,7 +1175,7 @@ class ServerCapabilities(Model):
11601175

11611176
# TODO typeHierarchyProvider?: boolean | TypeHierarchyOptions | TypeHierarchyRegistrationOptions;
11621177
inline_value_provider: Union[bool, InlineValueOptions, InlineValueRegistrationOptions, None] = None
1163-
# TODO inlayHintProvider?: boolean | InlayHintOptions | InlayHintRegistrationOptions;
1178+
inlay_hint_provider: Union[bool, InlayHintOptions, InlayHintRegistrationOptions, None] = None
11641179
diagnostic_provider: Union[DiagnosticOptions, DiagnosticRegistrationOptions, None] = None
11651180

11661181
workspace_symbol_provider: Union[bool, WorkspaceSymbolOptions, None] = None
@@ -2400,3 +2415,42 @@ class ApplyWorkspaceEditResult(Model):
24002415
applied: bool
24012416
failure_reason: Optional[str] = None
24022417
failed_change: Optional[int] = None
2418+
2419+
2420+
@dataclass(repr=False)
2421+
class _InlayHintParams(Model):
2422+
text_document: TextDocumentIdentifier
2423+
range: Range
2424+
2425+
2426+
@dataclass(repr=False)
2427+
class InlayHintParams(WorkDoneProgressParams, _InlayHintParams):
2428+
pass
2429+
2430+
2431+
@dataclass(repr=False)
2432+
class InlayHintLabelPart(Model):
2433+
value: str
2434+
tooltip: Union[str, MarkupContent]
2435+
location: Optional[Location] = None
2436+
command: Optional[Command] = None
2437+
2438+
2439+
class InlayHintKind(IntEnum):
2440+
TYPE = 1
2441+
PARAMETER = 2
2442+
2443+
def __repr__(self) -> str: # pragma: no cover
2444+
return super().__str__()
2445+
2446+
2447+
@dataclass(repr=False)
2448+
class InlayHint(Model):
2449+
position: Position
2450+
label: Union[str, List[InlayHintLabelPart]]
2451+
kind: Optional[InlayHintKind] = None
2452+
text_edits: Optional[List[TextEdit]] = None
2453+
tooltip: Union[str, MarkupContent, None] = None
2454+
padding_left: Optional[bool] = None
2455+
padding_right: Optional[bool] = None
2456+
data: LSPAny = None
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from __future__ import annotations
2+
3+
from asyncio import CancelledError
4+
from typing import TYPE_CHECKING, Any, List, Optional
5+
6+
from ....jsonrpc2.protocol import rpc_method
7+
from ....utils.async_tools import async_tasking_event, threaded
8+
from ....utils.logging import LoggingDescriptor
9+
from ..decorators import language_id_filter
10+
from ..has_extend_capabilities import HasExtendCapabilities
11+
from ..lsp_types import (
12+
InlayHint,
13+
InlayHintOptions,
14+
InlayHintParams,
15+
Range,
16+
ServerCapabilities,
17+
TextDocumentIdentifier,
18+
)
19+
from ..text_document import TextDocument
20+
21+
if TYPE_CHECKING:
22+
from ..protocol import LanguageServerProtocol
23+
24+
from .protocol_part import LanguageServerProtocolPart
25+
26+
27+
class InlayHintProtocolPart(LanguageServerProtocolPart, HasExtendCapabilities):
28+
29+
_logger = LoggingDescriptor()
30+
31+
def __init__(self, parent: LanguageServerProtocol) -> None:
32+
super().__init__(parent)
33+
34+
@async_tasking_event
35+
async def collect(sender, document: TextDocument, range: Range) -> Optional[List[InlayHint]]: # NOSONAR
36+
...
37+
38+
@async_tasking_event
39+
async def resolve(sender, hint: InlayHint) -> Optional[InlayHint]: # NOSONAR
40+
...
41+
42+
def extend_capabilities(self, capabilities: ServerCapabilities) -> None:
43+
if len(self.collect):
44+
if len(self.resolve):
45+
capabilities.inlay_hint_provider = InlayHintOptions(resolve_provider=bool(len(self.resolve)))
46+
else:
47+
capabilities.inlay_hint_provider = InlayHintOptions()
48+
49+
@rpc_method(name="textDocument/inlayHint", param_type=InlayHintParams)
50+
@threaded()
51+
async def _text_document_inlay_hint(
52+
self,
53+
text_document: TextDocumentIdentifier,
54+
range: Range,
55+
*args: Any,
56+
**kwargs: Any,
57+
) -> Optional[List[InlayHint]]:
58+
59+
results: List[InlayHint] = []
60+
61+
document = await self.parent.documents.get(text_document.uri)
62+
if document is None:
63+
return None
64+
65+
for result in await self.collect(
66+
self,
67+
document,
68+
range,
69+
callback_filter=language_id_filter(document),
70+
):
71+
if isinstance(result, BaseException):
72+
if not isinstance(result, CancelledError):
73+
self._logger.exception(result, exc_info=result)
74+
else:
75+
if result is not None:
76+
results.extend(result)
77+
78+
if len(results) > 0:
79+
return results
80+
81+
return None
82+
83+
@rpc_method(name="inlayHint/resolve", param_type=InlayHint)
84+
@threaded()
85+
async def _inlay_hint_resolve(
86+
self,
87+
params: InlayHint,
88+
*args: Any,
89+
**kwargs: Any,
90+
) -> Optional[InlayHint]:
91+
92+
for result in await self.resolve(self, params):
93+
if isinstance(result, BaseException):
94+
if not isinstance(result, CancelledError):
95+
self._logger.exception(result, exc_info=result)
96+
else:
97+
if isinstance(result, InlayHint):
98+
return result
99+
100+
return params
101+
102+
async def refresh(self) -> None:
103+
if (
104+
self.parent.client_capabilities is not None
105+
and self.parent.client_capabilities.workspace is not None
106+
and self.parent.client_capabilities.workspace.inlay_hint is not None
107+
and self.parent.client_capabilities.workspace.inlay_hint.refresh_support
108+
):
109+
await self.parent.send_request_async("workspace/inlayHint/refresh")

robotcode/language_server/common/protocol.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from .parts.formatting import FormattingProtocolPart
5353
from .parts.hover import HoverProtocolPart
5454
from .parts.implementation import ImplementationProtocolPart
55+
from .parts.inlay_hint import InlayHintProtocolPart
5556
from .parts.inline_value import InlineValueProtocolPart
5657
from .parts.linked_editing_ranges import LinkedEditingRangeProtocolPart
5758
from .parts.references import ReferencesProtocolPart
@@ -94,6 +95,7 @@ class LanguageServerProtocol(JsonRPCProtocol):
9495
selection_range = ProtocolPartDescriptor(SelectionRangeProtocolPart)
9596
rename = ProtocolPartDescriptor(RenameProtocolPart)
9697
inline_value = ProtocolPartDescriptor(InlineValueProtocolPart)
98+
inlay_hint = ProtocolPartDescriptor(InlayHintProtocolPart)
9799
code_action = ProtocolPartDescriptor(CodeActionProtocolPart)
98100

99101
name: Optional[str] = None

robotcode/language_server/robotframework/configuration.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ class DocumentationServerConfig(ConfigBase):
9090
end_port: int = 3199
9191

9292

93+
@config_section("robotcode.inlayHints")
94+
@dataclass
95+
class InlayHintsConfig(ConfigBase):
96+
parameter_names: bool = True
97+
namespaces: bool = True
98+
99+
93100
@config_section("robotcode")
94101
@dataclass
95102
class RobotCodeConfig(ConfigBase):
@@ -99,3 +106,4 @@ class RobotCodeConfig(ConfigBase):
99106
workspace: WorkspaceConfig = field(default_factory=WorkspaceConfig)
100107
analysis: AnalysisConfig = field(default_factory=AnalysisConfig)
101108
documentation_server: DocumentationServerConfig = field(default_factory=DocumentationServerConfig)
109+
inlay_hints: InlayHintsConfig = field(default_factory=InlayHintsConfig)

robotcode/language_server/robotframework/parts/completion.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ def get_keyword_snipped_text(self, kw: KeywordDoc, in_template: bool) -> str:
645645
return kw.name
646646

647647
result = ""
648+
after: Optional[str] = None
648649
for index, (before, variable, after) in enumerate(
649650
VariableIterator(kw.name, identifiers="$", ignore_errors=True)
650651
):
@@ -661,7 +662,8 @@ def get_keyword_snipped_text(self, kw: KeywordDoc, in_template: bool) -> str:
661662

662663
result += "}"
663664

664-
result += after
665+
if after:
666+
result += after
665667

666668
return result
667669

0 commit comments

Comments
 (0)