Skip to content

Commit 42ab3a0

Browse files
committed
feat(langserver): add code completion for Literal type hint values
Keywords with `Literal["fast", "slow", "auto"]` type hints now show their allowed values in the completion list, making it easier to discover and select valid argument values without checking the keyword documentation. Also supports `Union` types containing `Literal`, e.g. `Union[Literal["x", "y"], int]`. closes #211
1 parent e11ca60 commit 42ab3a0

30 files changed

+1571
-2
lines changed

.vscode/launch.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,10 @@
258258
"outFiles": [
259259
"${workspaceFolder}/out/**/*.js"
260260
],
261-
"preLaunchTask": "npm: compile"
261+
"preLaunchTask": "npm: compile",
262+
"env": {
263+
"PYDEVD_DISABLE_FILE_VALIDATION": "1"
264+
}
262265
},
263266
{
264267
"name": "Run Extension With Gherkin",

packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,6 +2393,20 @@ def _complete_keyword_arguments_at_position(
23932393
)
23942394
)
23952395

2396+
argument = kw_arguments[argument_index]
2397+
if argument.literal_values:
2398+
for literal_index, literal_value in enumerate(argument.literal_values):
2399+
result.append(
2400+
CompletionItem(
2401+
label=literal_value,
2402+
kind=CompletionItemKind.ENUM_MEMBER,
2403+
detail="Literal",
2404+
sort_text=f"09_{literal_index:09}_{literal_value}",
2405+
insert_text_format=InsertTextFormat.PLAIN_TEXT,
2406+
text_edit=TextEdit(range=completion_range, new_text=literal_value),
2407+
)
2408+
)
2409+
23962410
if complete_argument_names:
23972411
known_names = []
23982412

packages/robot/src/robotcode/robot/diagnostics/library_doc.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Iterable,
2525
Iterator,
2626
List,
27+
Literal,
2728
NamedTuple,
2829
Optional,
2930
Sequence,
@@ -467,6 +468,25 @@ class ArgumentInfo:
467468
required: bool
468469
default_value: Optional[Any] = None
469470
types: Optional[List[str]] = None
471+
literal_values: Optional[List[str]] = None
472+
473+
@staticmethod
474+
def _extract_literal_values(type_info: Any) -> Optional[List[str]]:
475+
if RF_VERSION < (7, 0) or type_info is None:
476+
return None
477+
478+
values: List[str] = []
479+
480+
def _collect(ti: Any) -> None:
481+
if ti.type is Literal and ti.nested:
482+
for nested in ti.nested:
483+
values.append(nested.name.strip("'"))
484+
elif ti.is_union and ti.nested:
485+
for nested in ti.nested:
486+
_collect(nested)
487+
488+
_collect(type_info)
489+
return values or None
470490

471491
@staticmethod
472492
def from_robot(arg: Any) -> ArgumentInfo:
@@ -487,6 +507,7 @@ def from_robot(arg: Any) -> ArgumentInfo:
487507
),
488508
kind=KeywordArgumentKind[robot_arg.kind],
489509
required=robot_arg.required,
510+
literal_values=ArgumentInfo._extract_literal_values(robot_arg.type if RF_VERSION >= (7, 0) else None),
490511
)
491512

492513
def __str__(self) -> str:
@@ -2323,7 +2344,7 @@ def get_args_to_process(libdoc_name: str, kw_name: str) -> Any:
23232344
def _yield_type_info(info: TypeInfo) -> Iterable[TypeInfo]:
23242345
if not info.is_union:
23252346
yield info
2326-
if info.nested:
2347+
if info.nested and info.type is not Literal:
23272348
for nested in info.nested:
23282349
yield from _yield_type_info(nested)
23292350

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
result: !SemanticTokens
2+
data:
3+
- 0
4+
- 0
5+
- 16
6+
- 26
7+
- 0
8+
- 1
9+
- 0
10+
- 7
11+
- 23
12+
- 0
13+
- 0
14+
- 11
15+
- 14
16+
- 51
17+
- 0
18+
- 2
19+
- 0
20+
- 18
21+
- 28
22+
- 0
23+
- 1
24+
- 0
25+
- 5
26+
- 32
27+
- 1
28+
- 1
29+
- 4
30+
- 22
31+
- 37
32+
- 0
33+
- 2
34+
- 0
35+
- 6
36+
- 32
37+
- 1
38+
- 1
39+
- 4
40+
- 12
41+
- 37
42+
- 0
43+
- 2
44+
- 0
45+
- 16
46+
- 31
47+
- 0
48+
- 1
49+
- 0
50+
- 22
51+
- 33
52+
- 1
53+
- 1
54+
- 4
55+
- 1
56+
- 21
57+
- 0
58+
- 0
59+
- 1
60+
- 9
61+
- 24
62+
- 0
63+
- 0
64+
- 9
65+
- 1
66+
- 21
67+
- 0
68+
- 0
69+
- 5
70+
- 23
71+
- 54
72+
- 0
73+
- 0
74+
- 27
75+
- 29
76+
- 54
77+
- 0
78+
- 1
79+
- 4
80+
- 12
81+
- 37
82+
- 0
83+
- 0
84+
- 14
85+
- 4
86+
- 36
87+
- 0
88+
- 1
89+
- 4
90+
- 12
91+
- 37
92+
- 0
93+
- 0
94+
- 14
95+
- 4
96+
- 36
97+
- 0
98+
result_id: null

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_workspace_symbols.test[].out

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,6 +3510,81 @@ result:
35103510
uri: jsonvariables.robot
35113511
name: first
35123512
tags: null
3513+
- !WorkspaceSymbol
3514+
container_name: literal
3515+
data: null
3516+
kind: 13
3517+
location:
3518+
range:
3519+
end:
3520+
character: 41
3521+
line: 12
3522+
start:
3523+
character: 21
3524+
line: 12
3525+
uri: literal.robot
3526+
name: '${a: Literal["q", "b"]}'
3527+
tags: null
3528+
- !WorkspaceSymbol
3529+
container_name: literal
3530+
data: null
3531+
kind: 13
3532+
location:
3533+
range:
3534+
end:
3535+
character: 74
3536+
line: 12
3537+
start:
3538+
character: 48
3539+
line: 12
3540+
uri: literal.robot
3541+
name: '${b: Literal["x", "y"] | int}'
3542+
tags: null
3543+
- !WorkspaceSymbol
3544+
container_name: literal
3545+
data: null
3546+
kind: 12
3547+
location:
3548+
range:
3549+
end:
3550+
character: 22
3551+
line: 11
3552+
start:
3553+
character: 0
3554+
line: 11
3555+
uri: literal.robot
3556+
name: A Keyword With Literal
3557+
tags: null
3558+
- !WorkspaceSymbol
3559+
container_name: literal
3560+
data: null
3561+
kind: 5
3562+
location:
3563+
range:
3564+
end:
3565+
character: 5
3566+
line: 4
3567+
start:
3568+
character: 0
3569+
line: 4
3570+
uri: literal.robot
3571+
name: first
3572+
tags: null
3573+
- !WorkspaceSymbol
3574+
container_name: literal
3575+
data: null
3576+
kind: 5
3577+
location:
3578+
range:
3579+
end:
3580+
character: 6
3581+
line: 7
3582+
start:
3583+
character: 0
3584+
line: 7
3585+
uri: literal.robot
3586+
name: second
3587+
tags: null
35133588
- !WorkspaceSymbol
35143589
container_name: old_for
35153590
data: null

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_workspace_symbols.test[first].out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,21 @@ result:
299299
uri: jsonvariables.robot
300300
name: first
301301
tags: null
302+
- !WorkspaceSymbol
303+
container_name: literal
304+
data: null
305+
kind: 5
306+
location:
307+
range:
308+
end:
309+
character: 5
310+
line: 4
311+
start:
312+
character: 0
313+
line: 4
314+
uri: literal.robot
315+
name: first
316+
tags: null
302317
- !WorkspaceSymbol
303318
container_name: old_for
304319
data: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
result: !SemanticTokens
2+
data:
3+
- 0
4+
- 0
5+
- 16
6+
- 26
7+
- 0
8+
- 1
9+
- 0
10+
- 7
11+
- 23
12+
- 0
13+
- 0
14+
- 11
15+
- 14
16+
- 51
17+
- 0
18+
- 2
19+
- 0
20+
- 18
21+
- 28
22+
- 0
23+
- 1
24+
- 0
25+
- 5
26+
- 32
27+
- 1
28+
- 1
29+
- 4
30+
- 22
31+
- 37
32+
- 0
33+
- 2
34+
- 0
35+
- 6
36+
- 32
37+
- 1
38+
- 1
39+
- 4
40+
- 12
41+
- 37
42+
- 0
43+
- 2
44+
- 0
45+
- 16
46+
- 31
47+
- 0
48+
- 1
49+
- 0
50+
- 22
51+
- 33
52+
- 1
53+
- 1
54+
- 4
55+
- 1
56+
- 21
57+
- 0
58+
- 0
59+
- 1
60+
- 9
61+
- 24
62+
- 0
63+
- 0
64+
- 9
65+
- 1
66+
- 21
67+
- 0
68+
- 0
69+
- 5
70+
- 23
71+
- 54
72+
- 0
73+
- 0
74+
- 27
75+
- 29
76+
- 54
77+
- 0
78+
- 1
79+
- 4
80+
- 12
81+
- 37
82+
- 0
83+
- 0
84+
- 14
85+
- 4
86+
- 36
87+
- 0
88+
- 1
89+
- 4
90+
- 12
91+
- 37
92+
- 0
93+
- 0
94+
- 14
95+
- 4
96+
- 36
97+
- 0
98+
result_id: null

0 commit comments

Comments
 (0)