robotcode-language-server 2.7.0__py3-none-any.whl → 2.8.0.dev8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- robotcode/language_server/__version__.py +1 -1
- robotcode/language_server/cli.py +30 -5
- robotcode/language_server/common/parts/diagnostics.py +50 -17
- robotcode/language_server/common/parts/documents.py +32 -26
- robotcode/language_server/common/parts/workspace.py +57 -55
- robotcode/language_server/common/protocol.py +3 -1
- robotcode/language_server/robotframework/configuration.py +3 -0
- robotcode/language_server/robotframework/parts/code_action_documentation.py +437 -146
- robotcode/language_server/robotframework/parts/code_action_helper_mixin.py +17 -2
- robotcode/language_server/robotframework/parts/code_action_quick_fixes.py +6 -25
- robotcode/language_server/robotframework/parts/code_action_refactor.py +8 -6
- robotcode/language_server/robotframework/parts/code_lens.py +3 -4
- robotcode/language_server/robotframework/parts/completion.py +193 -21
- robotcode/language_server/robotframework/parts/debugging_utils.py +75 -6
- robotcode/language_server/robotframework/parts/diagnostics.py +15 -3
- robotcode/language_server/robotframework/parts/documentation_viewer.py +127 -0
- robotcode/language_server/robotframework/parts/documents_cache.py +5 -1
- robotcode/language_server/robotframework/parts/hover.py +103 -21
- robotcode/language_server/robotframework/parts/http_server.py +17 -7
- robotcode/language_server/robotframework/parts/inline_value.py +69 -7
- robotcode/language_server/robotframework/parts/keywords_treeview.py +151 -53
- robotcode/language_server/robotframework/parts/model_variables.py +331 -0
- robotcode/language_server/robotframework/parts/references.py +107 -45
- robotcode/language_server/robotframework/parts/rename.py +1 -2
- robotcode/language_server/robotframework/parts/robot_workspace.py +229 -25
- robotcode/language_server/robotframework/parts/selection_range.py +26 -13
- robotcode/language_server/robotframework/parts/semantic_tokens.py +446 -113
- robotcode/language_server/robotframework/parts/signature_help.py +162 -70
- robotcode/language_server/robotframework/protocol.py +12 -1
- robotcode/language_server/robotframework/server.py +4 -2
- {robotcode_language_server-2.7.0.dist-info → robotcode_language_server-2.8.0.dev8.dist-info}/METADATA +7 -6
- {robotcode_language_server-2.7.0.dist-info → robotcode_language_server-2.8.0.dev8.dist-info}/RECORD +34 -32
- {robotcode_language_server-2.7.0.dist-info → robotcode_language_server-2.8.0.dev8.dist-info}/WHEEL +1 -1
- {robotcode_language_server-2.7.0.dist-info → robotcode_language_server-2.8.0.dev8.dist-info}/entry_points.txt +0 -0
|
@@ -21,6 +21,7 @@ from typing import (
|
|
|
21
21
|
Union,
|
|
22
22
|
cast,
|
|
23
23
|
)
|
|
24
|
+
from weakref import WeakKeyDictionary
|
|
24
25
|
|
|
25
26
|
from robot.parsing.lexer.tokens import Token
|
|
26
27
|
from robot.parsing.model.blocks import Section
|
|
@@ -39,7 +40,6 @@ from robot.parsing.model.statements import (
|
|
|
39
40
|
TestTemplate,
|
|
40
41
|
Variable,
|
|
41
42
|
VariablesImport,
|
|
42
|
-
WhileHeader,
|
|
43
43
|
)
|
|
44
44
|
from robot.utils.escaping import unescape
|
|
45
45
|
|
|
@@ -56,6 +56,7 @@ from robotcode.core.lsp.types import (
|
|
|
56
56
|
SemanticTokenTypes,
|
|
57
57
|
)
|
|
58
58
|
from robotcode.core.text_document import TextDocument, range_to_utf16
|
|
59
|
+
from robotcode.robot.diagnostics.entities import ArgumentDefinition, EmbeddedArgumentDefinition
|
|
59
60
|
from robotcode.robot.diagnostics.keyword_finder import DEFAULT_BDD_PREFIX_REGEXP, build_bdd_prefix_regexp
|
|
60
61
|
from robotcode.robot.diagnostics.library_doc import (
|
|
61
62
|
ALL_RUN_KEYWORDS_MATCHERS,
|
|
@@ -67,8 +68,14 @@ from robotcode.robot.diagnostics.library_doc import (
|
|
|
67
68
|
)
|
|
68
69
|
from robotcode.robot.diagnostics.model_helper import ModelHelper
|
|
69
70
|
from robotcode.robot.diagnostics.namespace import Namespace
|
|
70
|
-
from robotcode.robot.diagnostics.semantic_analyzer.enums import TokenKind
|
|
71
|
+
from robotcode.robot.diagnostics.semantic_analyzer.enums import NodeKind, TokenKind, TokenModifier
|
|
71
72
|
from robotcode.robot.diagnostics.semantic_analyzer.model import SemanticModel
|
|
73
|
+
from robotcode.robot.diagnostics.semantic_analyzer.nodes import (
|
|
74
|
+
RunKeywordCallStatement,
|
|
75
|
+
SemanticStatement,
|
|
76
|
+
SemanticToken,
|
|
77
|
+
)
|
|
78
|
+
from robotcode.robot.diagnostics.semantic_analyzer.variable_tokenizer import VariableSite, build_variable_sub_tokens
|
|
72
79
|
from robotcode.robot.utils import RF_VERSION
|
|
73
80
|
from robotcode.robot.utils.ast import (
|
|
74
81
|
cached_isinstance,
|
|
@@ -1034,46 +1041,51 @@ class SemanticTokenGenerator:
|
|
|
1034
1041
|
creating and managing its own token mapper and keyword analyzer.
|
|
1035
1042
|
"""
|
|
1036
1043
|
|
|
1037
|
-
#
|
|
1038
|
-
#
|
|
1039
|
-
|
|
1044
|
+
# ------------------------------------------------------------------
|
|
1045
|
+
# Model-based path (Tier 1): declarative rendering of the SemanticModel.
|
|
1046
|
+
#
|
|
1047
|
+
# After analysis every token carries its final render semantics (kind +
|
|
1048
|
+
# modifiers), so rendering is: leaf descent -> static table lookup ->
|
|
1049
|
+
# delta encoding, plus a small legacy-compat emission policy keyed only
|
|
1050
|
+
# on TokenKind / NodeKind / TokenModifier (design D6/D7). No RF version
|
|
1051
|
+
# checks, no tokenization, no statement-type semantics here.
|
|
1052
|
+
# ------------------------------------------------------------------
|
|
1053
|
+
|
|
1054
|
+
_TOKEN_KIND_TO_SEM_TOKEN: ClassVar[Dict[TokenKind, Tuple[AnyTokenType, Optional[Set[AnyTokenModifier]]]]] = {
|
|
1040
1055
|
TokenKind.KEYWORD: (RobotSemTokenTypes.KEYWORD, None),
|
|
1056
|
+
TokenKind.KEYWORD_INNER: (RobotSemTokenTypes.KEYWORD_INNER, None),
|
|
1041
1057
|
TokenKind.BDD_PREFIX: (RobotSemTokenTypes.BDD_PREFIX, None),
|
|
1042
1058
|
TokenKind.NAMESPACE: (RobotSemTokenTypes.NAMESPACE, None),
|
|
1059
|
+
# Variables render their name only (see `_render_variable_part`).
|
|
1043
1060
|
TokenKind.VARIABLE: (RobotSemTokenTypes.VARIABLE, None),
|
|
1044
1061
|
TokenKind.VARIABLE_NOT_FOUND: (RobotSemTokenTypes.VARIABLE, None),
|
|
1045
|
-
TokenKind.
|
|
1046
|
-
TokenKind.VARIABLE_OPEN_BRACE: (RobotSemTokenTypes.VARIABLE_BEGIN, None),
|
|
1047
|
-
TokenKind.VARIABLE_CLOSE_BRACE: (RobotSemTokenTypes.VARIABLE_END, None),
|
|
1048
|
-
TokenKind.VARIABLE_BASE: (RobotSemTokenTypes.VARIABLE, None),
|
|
1049
|
-
TokenKind.VARIABLE_EXTENDED: (RobotSemTokenTypes.VARIABLE, None),
|
|
1050
|
-
TokenKind.VARIABLE_TYPE_SEPARATOR: (SemanticTokenTypes.OPERATOR, None),
|
|
1051
|
-
TokenKind.VARIABLE_TYPE_HINT: (SemanticTokenTypes.TYPE, None),
|
|
1052
|
-
TokenKind.VARIABLE_DEFAULT_SEPARATOR: (SemanticTokenTypes.OPERATOR, None),
|
|
1062
|
+
TokenKind.VARIABLE_NAME: (RobotSemTokenTypes.VARIABLE, None),
|
|
1053
1063
|
TokenKind.VARIABLE_DEFAULT_VALUE: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1054
|
-
TokenKind.VARIABLE_PATTERN_SEPARATOR: (SemanticTokenTypes.OPERATOR, None),
|
|
1055
1064
|
TokenKind.VARIABLE_PATTERN: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1056
|
-
TokenKind.VARIABLE_ASSIGN_MARK: (RobotSemTokenTypes.VARIABLE, None),
|
|
1057
|
-
TokenKind.VARIABLE_EXPRESSION_OPEN: (RobotSemTokenTypes.EXPRESSION_BEGIN, None),
|
|
1058
|
-
TokenKind.VARIABLE_EXPRESSION_CLOSE: (RobotSemTokenTypes.EXPRESSION_END, None),
|
|
1059
|
-
TokenKind.PYTHON_EXPRESSION: (RobotSemTokenTypes.VARIABLE_EXPRESSION, None),
|
|
1060
|
-
TokenKind.PYTHON_VARIABLE_REF: (RobotSemTokenTypes.VARIABLE, None),
|
|
1061
|
-
TokenKind.VARIABLE_INDEX: (RobotSemTokenTypes.VARIABLE, None),
|
|
1062
|
-
TokenKind.VARIABLE_INDEX_OPEN: (RobotSemTokenTypes.VARIABLE_BEGIN, None),
|
|
1063
|
-
TokenKind.VARIABLE_INDEX_CLOSE: (RobotSemTokenTypes.VARIABLE_END, None),
|
|
1064
|
-
TokenKind.VARIABLE_INDEX_CONTENT: (RobotSemTokenTypes.VARIABLE, None),
|
|
1065
1065
|
TokenKind.TEXT_FRAGMENT: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1066
1066
|
TokenKind.ARGUMENT: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1067
1067
|
TokenKind.NAMED_ARGUMENT_NAME: (RobotSemTokenTypes.NAMED_ARGUMENT, None),
|
|
1068
1068
|
TokenKind.NAMED_ARGUMENT_VALUE: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1069
|
+
TokenKind.PARAMETER: (SemanticTokenTypes.PARAMETER, None),
|
|
1069
1070
|
TokenKind.CONTROL_FLOW: (RobotSemTokenTypes.CONTROL_FLOW, None),
|
|
1070
1071
|
TokenKind.CONDITION: (RobotSemTokenTypes.ARGUMENT, None),
|
|
1072
|
+
TokenKind.FOR_SEPARATOR: (RobotSemTokenTypes.FOR_SEPARATOR, None),
|
|
1073
|
+
TokenKind.VAR_MARKER: (RobotSemTokenTypes.VAR, None),
|
|
1074
|
+
TokenKind.OPTION: (RobotSemTokenTypes.CONTROL_FLOW, None),
|
|
1075
|
+
TokenKind.OPTION_VALUE: (RobotSemTokenTypes.CONTROL_FLOW, None),
|
|
1071
1076
|
TokenKind.TEST_NAME: (RobotSemTokenTypes.TESTCASE_NAME, {SemanticTokenModifiers.DECLARATION}),
|
|
1072
1077
|
TokenKind.KEYWORD_NAME: (RobotSemTokenTypes.KEYWORD_NAME, {SemanticTokenModifiers.DECLARATION}),
|
|
1073
|
-
TokenKind.VARIABLE_NAME: (RobotSemTokenTypes.VARIABLE, {SemanticTokenModifiers.DECLARATION}),
|
|
1074
1078
|
TokenKind.SETTING_NAME: (RobotSemTokenTypes.SETTING, None),
|
|
1075
|
-
TokenKind.
|
|
1079
|
+
TokenKind.SETTING_IMPORT: (RobotSemTokenTypes.SETTING_IMPORT, None),
|
|
1080
|
+
TokenKind.IMPORT_NAME: (RobotSemTokenTypes.NAMESPACE, None),
|
|
1081
|
+
TokenKind.OPERATOR: (SemanticTokenTypes.OPERATOR, None),
|
|
1076
1082
|
TokenKind.HEADER: (RobotSemTokenTypes.HEADER, None),
|
|
1083
|
+
TokenKind.HEADER_SETTINGS: (RobotSemTokenTypes.HEADER_SETTINGS, None),
|
|
1084
|
+
TokenKind.HEADER_VARIABLE: (RobotSemTokenTypes.HEADER_VARIABLE, None),
|
|
1085
|
+
TokenKind.HEADER_TESTCASE: (RobotSemTokenTypes.HEADER_TESTCASE, None),
|
|
1086
|
+
TokenKind.HEADER_TASK: (RobotSemTokenTypes.HEADER_TASK, None),
|
|
1087
|
+
TokenKind.HEADER_KEYWORD: (RobotSemTokenTypes.HEADER_KEYWORD, None),
|
|
1088
|
+
TokenKind.HEADER_COMMENT: (RobotSemTokenTypes.HEADER_COMMENT, None),
|
|
1077
1089
|
TokenKind.SEPARATOR: (RobotSemTokenTypes.SEPARATOR, None),
|
|
1078
1090
|
TokenKind.CONTINUATION: (RobotSemTokenTypes.CONTINUATION, None),
|
|
1079
1091
|
TokenKind.COMMENT: (SemanticTokenTypes.COMMENT, None),
|
|
@@ -1082,10 +1094,73 @@ class SemanticTokenGenerator:
|
|
|
1082
1094
|
TokenKind.ERROR: (RobotSemTokenTypes.ERROR, None),
|
|
1083
1095
|
}
|
|
1084
1096
|
|
|
1097
|
+
_TOKEN_MODIFIER_TO_SEM: ClassVar[Dict[TokenModifier, AnyTokenModifier]] = {
|
|
1098
|
+
TokenModifier.BUILTIN: RobotSemTokenModifiers.BUILTIN,
|
|
1099
|
+
TokenModifier.EMBEDDED: RobotSemTokenModifiers.EMBEDDED,
|
|
1100
|
+
TokenModifier.DECLARATION: SemanticTokenModifiers.DECLARATION,
|
|
1101
|
+
TokenModifier.DOCUMENTATION: SemanticTokenModifiers.DOCUMENTATION,
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
# Modifiers that change the type instead: a variable that names an argument
|
|
1105
|
+
# renders as `parameter`, like the argument's declaration.
|
|
1106
|
+
_TOKEN_MODIFIER_TO_SEM_TYPE: ClassVar[Dict[Tuple[TokenKind, TokenModifier], AnyTokenType]] = {
|
|
1107
|
+
(TokenKind.VARIABLE, TokenModifier.ARGUMENT): SemanticTokenTypes.PARAMETER,
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
# --- Legacy-compat emission policy (design D7) ---
|
|
1111
|
+
|
|
1112
|
+
# Whole options of control structures render atomically: one control-flow
|
|
1113
|
+
# token per option, no descent into their sub-structure.
|
|
1114
|
+
_ATOMIC_KINDS: ClassVar[FrozenSet[TokenKind]] = frozenset({TokenKind.OPTION, TokenKind.OPTION_VALUE})
|
|
1115
|
+
# Variables and parameters render only their name (and type hint); the
|
|
1116
|
+
# grammar shows prefix, braces, item access, `=`, patterns, defaults and
|
|
1117
|
+
# inline Python.
|
|
1118
|
+
_VARIABLE_KINDS: ClassVar[FrozenSet[TokenKind]] = frozenset(
|
|
1119
|
+
{TokenKind.VARIABLE, TokenKind.VARIABLE_NOT_FOUND, TokenKind.VARIABLE_NAME, TokenKind.PARAMETER}
|
|
1120
|
+
)
|
|
1121
|
+
# Model-only kinds: carried for model consumers (inline values, debug
|
|
1122
|
+
# variable extraction), never rendered — legacy highlights no bare-`$var`
|
|
1123
|
+
# expression references.
|
|
1124
|
+
_MODEL_ONLY_KINDS: ClassVar[FrozenSet[TokenKind]] = frozenset({TokenKind.PYTHON_VARIABLE_REF})
|
|
1125
|
+
# Plain argument text is suppressed by the legacy path...
|
|
1126
|
+
_ARGUMENT_TEXT_KINDS: ClassVar[FrozenSet[TokenKind]] = frozenset(
|
|
1127
|
+
{
|
|
1128
|
+
TokenKind.ARGUMENT,
|
|
1129
|
+
TokenKind.TEXT_FRAGMENT,
|
|
1130
|
+
TokenKind.TAG,
|
|
1131
|
+
TokenKind.CONDITION,
|
|
1132
|
+
TokenKind.NAMED_ARGUMENT_VALUE,
|
|
1133
|
+
TokenKind.VARIABLE_DEFAULT_VALUE,
|
|
1134
|
+
TokenKind.VARIABLE_PATTERN,
|
|
1135
|
+
}
|
|
1136
|
+
)
|
|
1137
|
+
# ...except in template argument rows and metadata values...
|
|
1138
|
+
# (TEMPLATE_KEYWORD: an unresolved template name keeps kind ARGUMENT and
|
|
1139
|
+
# must still render, matching legacy.)
|
|
1140
|
+
_YIELD_ARGUMENT_STMT_KINDS: ClassVar[FrozenSet[NodeKind]] = frozenset(
|
|
1141
|
+
{NodeKind.TEMPLATE_DATA, NodeKind.SETTING_METADATA, NodeKind.TEMPLATE_KEYWORD}
|
|
1142
|
+
)
|
|
1143
|
+
# Comments only render on keyword-call / import statements (and inside
|
|
1144
|
+
# invalid sections).
|
|
1145
|
+
_COMMENT_STMT_KINDS: ClassVar[FrozenSet[NodeKind]] = frozenset(
|
|
1146
|
+
{NodeKind.KEYWORD_CALL, NodeKind.SETUP, NodeKind.TEARDOWN, NodeKind.TEMPLATE_KEYWORD, NodeKind.IMPORT}
|
|
1147
|
+
)
|
|
1148
|
+
# Statements whose keyword name comes from a Token.NAME cell -- their
|
|
1149
|
+
# BDD-prefix gap token renders as `argument` (the NAME cell's initial
|
|
1150
|
+
# legacy mapping), not as a keyword.
|
|
1151
|
+
_NAME_KEYWORD_STMT_KINDS: ClassVar[FrozenSet[NodeKind]] = frozenset(
|
|
1152
|
+
{NodeKind.SETUP, NodeKind.TEARDOWN, NodeKind.TEMPLATE_KEYWORD}
|
|
1153
|
+
)
|
|
1154
|
+
_BDD_FOLLOW_KINDS: ClassVar[FrozenSet[TokenKind]] = frozenset(
|
|
1155
|
+
{TokenKind.KEYWORD, TokenKind.KEYWORD_INNER, TokenKind.NAMESPACE, TokenKind.ARGUMENT}
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1085
1158
|
def __init__(self) -> None:
|
|
1086
1159
|
"""Initialize the generator with its own dependencies."""
|
|
1087
1160
|
self.token_mapper = SemanticTokenMapper()
|
|
1088
1161
|
self.keyword_analyzer = KeywordTokenAnalyzer(self.token_mapper)
|
|
1162
|
+
# a namespace holds the result of one analysis, so its argument uses never change
|
|
1163
|
+
self._argument_uses_cache: WeakKeyDictionary[Namespace, FrozenSet[Tuple[int, int]]] = WeakKeyDictionary()
|
|
1089
1164
|
|
|
1090
1165
|
def collect_tokens_from_model(
|
|
1091
1166
|
self,
|
|
@@ -1098,8 +1173,9 @@ class SemanticTokenGenerator:
|
|
|
1098
1173
|
"""Collect semantic tokens from the pre-built SemanticModel.
|
|
1099
1174
|
|
|
1100
1175
|
This is the Tier 1 model-based path, used when the semantic model
|
|
1101
|
-
feature flag is enabled.
|
|
1102
|
-
|
|
1176
|
+
feature flag is enabled. The model carries final render semantics,
|
|
1177
|
+
so this is a declarative mapping (see the class-level tables) plus
|
|
1178
|
+
UTF-16 delta encoding.
|
|
1103
1179
|
"""
|
|
1104
1180
|
data: List[int] = []
|
|
1105
1181
|
last_line = 0
|
|
@@ -1109,30 +1185,21 @@ class SemanticTokenGenerator:
|
|
|
1109
1185
|
for stmt in semantic_model.statements:
|
|
1110
1186
|
check_current_task_canceled()
|
|
1111
1187
|
|
|
1112
|
-
for
|
|
1113
|
-
if
|
|
1188
|
+
for info in self._iter_model_sem_tokens(stmt, semantic_model):
|
|
1189
|
+
if info.length == 0:
|
|
1114
1190
|
continue
|
|
1115
1191
|
|
|
1192
|
+
token_line_0 = info.lineno - 1
|
|
1116
1193
|
# Range filtering
|
|
1117
|
-
token_line_0
|
|
1118
|
-
if range is not None:
|
|
1119
|
-
if token_line_0 < range.start.line:
|
|
1120
|
-
continue
|
|
1121
|
-
if token_line_0 > range.end.line:
|
|
1122
|
-
break
|
|
1123
|
-
|
|
1124
|
-
sem_info = self._TOKEN_KIND_TO_SEM_TOKEN.get(token.kind)
|
|
1125
|
-
if sem_info is None:
|
|
1194
|
+
if range is not None and (token_line_0 < range.start.line or token_line_0 > range.end.line):
|
|
1126
1195
|
continue
|
|
1127
1196
|
|
|
1128
|
-
sem_type, sem_mods = sem_info
|
|
1129
|
-
|
|
1130
1197
|
# Convert to UTF-16 positions
|
|
1131
1198
|
token_range = range_to_utf16(
|
|
1132
1199
|
lines,
|
|
1133
1200
|
Range(
|
|
1134
|
-
start=Position(line=token_line_0, character=
|
|
1135
|
-
end=Position(line=token_line_0, character=
|
|
1201
|
+
start=Position(line=token_line_0, character=info.col_offset),
|
|
1202
|
+
end=Position(line=token_line_0, character=info.col_offset + info.length),
|
|
1136
1203
|
),
|
|
1137
1204
|
)
|
|
1138
1205
|
|
|
@@ -1154,11 +1221,246 @@ class SemanticTokenGenerator:
|
|
|
1154
1221
|
last_line = current_line
|
|
1155
1222
|
|
|
1156
1223
|
data.append(token_length)
|
|
1157
|
-
data.append(token_types.index(
|
|
1158
|
-
data.append(
|
|
1224
|
+
data.append(token_types.index(info.sem_token_type))
|
|
1225
|
+
data.append(
|
|
1226
|
+
reduce(operator.or_, [2 ** token_modifiers.index(e) for e in info.sem_modifiers])
|
|
1227
|
+
if info.sem_modifiers
|
|
1228
|
+
else 0
|
|
1229
|
+
)
|
|
1159
1230
|
|
|
1160
1231
|
return SemanticTokens(data=data)
|
|
1161
1232
|
|
|
1233
|
+
def _emit_model_token(self, token: SemanticToken) -> Optional[SemTokenInfo]:
|
|
1234
|
+
"""Map one model token through the static kind / modifier tables."""
|
|
1235
|
+
sem_info = self._TOKEN_KIND_TO_SEM_TOKEN.get(token.kind)
|
|
1236
|
+
if sem_info is None:
|
|
1237
|
+
return None
|
|
1238
|
+
sem_type, static_mods = sem_info
|
|
1239
|
+
sem_mods: Optional[Set[AnyTokenModifier]] = set(static_mods) if static_mods else None
|
|
1240
|
+
if token.modifiers:
|
|
1241
|
+
mapped = {self._TOKEN_MODIFIER_TO_SEM[m] for m in token.modifiers if m in self._TOKEN_MODIFIER_TO_SEM}
|
|
1242
|
+
if mapped:
|
|
1243
|
+
sem_mods = (sem_mods or set()) | mapped
|
|
1244
|
+
for m in token.modifiers:
|
|
1245
|
+
sem_type = self._TOKEN_MODIFIER_TO_SEM_TYPE.get((token.kind, m), sem_type)
|
|
1246
|
+
return SemTokenInfo(token.line, token.col_offset, token.length, sem_type, sem_mods)
|
|
1247
|
+
|
|
1248
|
+
def _iter_model_sem_tokens(
|
|
1249
|
+
self, stmt: SemanticStatement, model: SemanticModel, is_inner: bool = False
|
|
1250
|
+
) -> Iterator[SemTokenInfo]:
|
|
1251
|
+
"""Yield SemTokenInfo for one statement, matching the legacy output.
|
|
1252
|
+
|
|
1253
|
+
`is_inner` marks a Run Keyword inner call (its BDD gap token renders
|
|
1254
|
+
as keywordCallInner).
|
|
1255
|
+
"""
|
|
1256
|
+
# Documentation statements emit only their continuation markers
|
|
1257
|
+
# (legacy renders neither the setting name nor the body).
|
|
1258
|
+
if stmt.kind is NodeKind.SETTING_DOCUMENTATION:
|
|
1259
|
+
for token in stmt.tokens:
|
|
1260
|
+
if token.kind is TokenKind.CONTINUATION:
|
|
1261
|
+
yield SemTokenInfo(token.line, token.col_offset, token.length, RobotSemTokenTypes.CONTINUATION)
|
|
1262
|
+
return
|
|
1263
|
+
|
|
1264
|
+
section = model.enclosing_section(stmt)
|
|
1265
|
+
emit_comments = stmt.kind in self._COMMENT_STMT_KINDS or (
|
|
1266
|
+
section is not None and section.kind is NodeKind.INVALID_SECTION
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
if cached_isinstance(stmt, RunKeywordCallStatement):
|
|
1270
|
+
kw_doc = stmt.keyword_doc
|
|
1271
|
+
# Legacy only decomposes the hardcoded BuiltIn run keywords;
|
|
1272
|
+
# register-only run keywords render flat.
|
|
1273
|
+
if kw_doc is not None and kw_doc.is_any_run_keyword():
|
|
1274
|
+
yield from self._iter_run_keyword_sem_tokens(stmt, model, emit_comments, is_inner)
|
|
1275
|
+
return
|
|
1276
|
+
|
|
1277
|
+
yield_arguments = stmt.kind in self._YIELD_ARGUMENT_STMT_KINDS
|
|
1278
|
+
|
|
1279
|
+
prev: Optional[SemanticToken] = None
|
|
1280
|
+
for token in stmt.tokens:
|
|
1281
|
+
gap = self._bdd_gap_token(stmt, prev, token, is_inner)
|
|
1282
|
+
if gap is not None:
|
|
1283
|
+
yield gap
|
|
1284
|
+
yield from self._render_model_token(token, emit_comments, yield_arguments)
|
|
1285
|
+
prev = token
|
|
1286
|
+
|
|
1287
|
+
def _bdd_gap_token(
|
|
1288
|
+
self,
|
|
1289
|
+
stmt: SemanticStatement,
|
|
1290
|
+
prev: Optional[SemanticToken],
|
|
1291
|
+
token: SemanticToken,
|
|
1292
|
+
is_inner: bool,
|
|
1293
|
+
) -> Optional[SemTokenInfo]:
|
|
1294
|
+
"""Legacy quirk: a BDD prefix (`Given`/`When`/...) emits a length-1
|
|
1295
|
+
keyword-typed token for the separator character between the prefix
|
|
1296
|
+
word and the keyword name. The model folds that space into the prefix
|
|
1297
|
+
offset, so it is synthesized here from adjacent leaf kinds."""
|
|
1298
|
+
if (
|
|
1299
|
+
prev is None
|
|
1300
|
+
or prev.kind is not TokenKind.BDD_PREFIX
|
|
1301
|
+
or token.kind not in self._BDD_FOLLOW_KINDS
|
|
1302
|
+
or token.col_offset <= prev.col_offset + prev.length
|
|
1303
|
+
):
|
|
1304
|
+
return None
|
|
1305
|
+
if is_inner:
|
|
1306
|
+
sem_type: AnyTokenType = RobotSemTokenTypes.KEYWORD_INNER
|
|
1307
|
+
elif stmt.kind in self._NAME_KEYWORD_STMT_KINDS:
|
|
1308
|
+
sem_type = RobotSemTokenTypes.ARGUMENT
|
|
1309
|
+
else:
|
|
1310
|
+
sem_type = RobotSemTokenTypes.KEYWORD
|
|
1311
|
+
gap_col = prev.col_offset + prev.length
|
|
1312
|
+
return SemTokenInfo(prev.line, gap_col, token.col_offset - gap_col, sem_type)
|
|
1313
|
+
|
|
1314
|
+
def _render_model_token(
|
|
1315
|
+
self,
|
|
1316
|
+
token: SemanticToken,
|
|
1317
|
+
emit_comments: bool,
|
|
1318
|
+
yield_arguments: bool,
|
|
1319
|
+
) -> Iterator[SemTokenInfo]:
|
|
1320
|
+
"""Render one SemanticToken (recursively descending into sub-tokens)
|
|
1321
|
+
through the static tables and the declarative emission policy."""
|
|
1322
|
+
kind = token.kind
|
|
1323
|
+
mods = token.modifiers
|
|
1324
|
+
|
|
1325
|
+
if kind in self._MODEL_ONLY_KINDS:
|
|
1326
|
+
return
|
|
1327
|
+
|
|
1328
|
+
if kind in self._VARIABLE_KINDS and token.sub_tokens:
|
|
1329
|
+
for sub in token.sub_tokens:
|
|
1330
|
+
yield from self._render_variable_part(sub, token)
|
|
1331
|
+
return
|
|
1332
|
+
|
|
1333
|
+
# Atomic kinds render whole; their sub-structure stays model-only.
|
|
1334
|
+
if kind in self._ATOMIC_KINDS:
|
|
1335
|
+
info = self._emit_model_token(token)
|
|
1336
|
+
if info is not None:
|
|
1337
|
+
yield info
|
|
1338
|
+
return
|
|
1339
|
+
|
|
1340
|
+
# Legacy quirk: `[Documentation]` / `Metadata` setting names render
|
|
1341
|
+
# as nothing.
|
|
1342
|
+
if kind is TokenKind.SETTING_NAME and mods and TokenModifier.DOCUMENTATION in mods:
|
|
1343
|
+
return
|
|
1344
|
+
|
|
1345
|
+
# Legacy quirk: an embedded keyword name that does not match its own
|
|
1346
|
+
# pattern (parent carries EMBEDDED, no sub-tokens) renders as nothing.
|
|
1347
|
+
if (
|
|
1348
|
+
kind in (TokenKind.KEYWORD, TokenKind.KEYWORD_INNER)
|
|
1349
|
+
and mods
|
|
1350
|
+
and TokenModifier.EMBEDDED in mods
|
|
1351
|
+
and not token.sub_tokens
|
|
1352
|
+
):
|
|
1353
|
+
return
|
|
1354
|
+
|
|
1355
|
+
# Leaf descent: emit the leaves instead of the parent.
|
|
1356
|
+
if token.sub_tokens:
|
|
1357
|
+
for sub in token.sub_tokens:
|
|
1358
|
+
yield from self._render_model_token(sub, emit_comments, yield_arguments)
|
|
1359
|
+
return
|
|
1360
|
+
|
|
1361
|
+
# Whitespace / line-structure tokens are never rendered.
|
|
1362
|
+
if kind is TokenKind.SEPARATOR or kind is TokenKind.EOL:
|
|
1363
|
+
return
|
|
1364
|
+
|
|
1365
|
+
if kind is TokenKind.COMMENT:
|
|
1366
|
+
if emit_comments:
|
|
1367
|
+
yield SemTokenInfo(token.line, token.col_offset, token.length, SemanticTokenTypes.COMMENT)
|
|
1368
|
+
return
|
|
1369
|
+
|
|
1370
|
+
# Plain argument text renders only in template rows / metadata values
|
|
1371
|
+
# and for embedded-argument fragments.
|
|
1372
|
+
if kind in self._ARGUMENT_TEXT_KINDS and not yield_arguments and not (mods and TokenModifier.EMBEDDED in mods):
|
|
1373
|
+
return
|
|
1374
|
+
|
|
1375
|
+
info = self._emit_model_token(token)
|
|
1376
|
+
if info is not None:
|
|
1377
|
+
yield info
|
|
1378
|
+
|
|
1379
|
+
def _render_variable_part(self, token: SemanticToken, variable: SemanticToken) -> Iterator[SemTokenInfo]:
|
|
1380
|
+
"""Render one sub-token of `variable`: its name with the type and
|
|
1381
|
+
modifiers of the variable, its type hint as `type`, nested variables as
|
|
1382
|
+
variables of their own, and nothing else."""
|
|
1383
|
+
kind = token.kind
|
|
1384
|
+
if kind in self._VARIABLE_KINDS:
|
|
1385
|
+
yield from self._render_model_token(token, emit_comments=False, yield_arguments=False)
|
|
1386
|
+
return
|
|
1387
|
+
if kind is TokenKind.PYTHON_EXPRESSION:
|
|
1388
|
+
return
|
|
1389
|
+
if token.sub_tokens:
|
|
1390
|
+
for sub in token.sub_tokens:
|
|
1391
|
+
yield from self._render_variable_part(sub, variable)
|
|
1392
|
+
return
|
|
1393
|
+
if kind is TokenKind.VARIABLE_BASE:
|
|
1394
|
+
info = self._emit_model_token(variable)
|
|
1395
|
+
if info is not None:
|
|
1396
|
+
yield SemTokenInfo(token.line, token.col_offset, token.length, info.sem_token_type, info.sem_modifiers)
|
|
1397
|
+
elif kind is TokenKind.VARIABLE_TYPE_HINT:
|
|
1398
|
+
yield SemTokenInfo(token.line, token.col_offset, token.length, SemanticTokenTypes.TYPE)
|
|
1399
|
+
|
|
1400
|
+
def _iter_run_keyword_sem_tokens(
|
|
1401
|
+
self,
|
|
1402
|
+
stmt: "RunKeywordCallStatement",
|
|
1403
|
+
model: SemanticModel,
|
|
1404
|
+
emit_comments: bool,
|
|
1405
|
+
is_inner: bool,
|
|
1406
|
+
) -> Iterator[SemTokenInfo]:
|
|
1407
|
+
"""Render a Run Keyword variant, position-merging inner calls with the
|
|
1408
|
+
outer token stream. Cells covered by an inner call render from the
|
|
1409
|
+
inner (KEYWORD_INNER) decomposition; the remaining outer tokens (outer
|
|
1410
|
+
keyword, condition, ELSE/AND control-flow cells) render from the outer
|
|
1411
|
+
list. The merge is purely structural."""
|
|
1412
|
+
inner_infos: List[SemTokenInfo] = []
|
|
1413
|
+
covered: List[Tuple[Tuple[int, int], Tuple[int, int]]] = []
|
|
1414
|
+
for inner in stmt.inner_calls:
|
|
1415
|
+
span = self._statement_span(inner)
|
|
1416
|
+
if span is not None:
|
|
1417
|
+
covered.append(span)
|
|
1418
|
+
inner_infos.extend(self._iter_model_sem_tokens(inner, model, is_inner=True))
|
|
1419
|
+
|
|
1420
|
+
outer_infos: List[SemTokenInfo] = []
|
|
1421
|
+
prev: Optional[SemanticToken] = None
|
|
1422
|
+
for token in stmt.tokens:
|
|
1423
|
+
pos = (token.line, token.col_offset)
|
|
1424
|
+
if any(start <= pos < end for start, end in covered):
|
|
1425
|
+
prev = token
|
|
1426
|
+
continue
|
|
1427
|
+
gap = self._bdd_gap_token(stmt, prev, token, is_inner)
|
|
1428
|
+
if gap is not None:
|
|
1429
|
+
outer_infos.append(gap)
|
|
1430
|
+
outer_infos.extend(self._render_model_token(token, emit_comments, False))
|
|
1431
|
+
prev = token
|
|
1432
|
+
|
|
1433
|
+
merged = inner_infos + outer_infos
|
|
1434
|
+
merged.sort(key=lambda i: (i.lineno, i.col_offset))
|
|
1435
|
+
yield from merged
|
|
1436
|
+
|
|
1437
|
+
@staticmethod
|
|
1438
|
+
def _statement_span(
|
|
1439
|
+
stmt: SemanticStatement,
|
|
1440
|
+
) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]:
|
|
1441
|
+
"""Source span (start, end) as (line, col) tuples over all tokens of a
|
|
1442
|
+
statement, recursing into nested Run Keyword inner calls."""
|
|
1443
|
+
start: Optional[Tuple[int, int]] = None
|
|
1444
|
+
end: Optional[Tuple[int, int]] = None
|
|
1445
|
+
|
|
1446
|
+
def visit(s: SemanticStatement) -> None:
|
|
1447
|
+
nonlocal start, end
|
|
1448
|
+
for token in s.tokens:
|
|
1449
|
+
lo = (token.line, token.col_offset)
|
|
1450
|
+
hi = (token.line, token.col_offset + token.length)
|
|
1451
|
+
if start is None or lo < start:
|
|
1452
|
+
start = lo
|
|
1453
|
+
if end is None or hi > end:
|
|
1454
|
+
end = hi
|
|
1455
|
+
if cached_isinstance(s, RunKeywordCallStatement):
|
|
1456
|
+
for inner in s.inner_calls:
|
|
1457
|
+
visit(inner)
|
|
1458
|
+
|
|
1459
|
+
visit(stmt)
|
|
1460
|
+
if start is None or end is None:
|
|
1461
|
+
return None
|
|
1462
|
+
return start, end
|
|
1463
|
+
|
|
1162
1464
|
def _get_tokens_after(self, tokens: List[Token], target_token: Token) -> List[Token]:
|
|
1163
1465
|
"""Get all tokens after target token efficiently.
|
|
1164
1466
|
|
|
@@ -1194,6 +1496,7 @@ class SemanticTokenGenerator:
|
|
|
1194
1496
|
col_offset: Optional[int] = None,
|
|
1195
1497
|
length: Optional[int] = None,
|
|
1196
1498
|
yield_arguments: bool = False,
|
|
1499
|
+
site: VariableSite = VariableSite.USAGE,
|
|
1197
1500
|
) -> Iterator[SemTokenInfo]:
|
|
1198
1501
|
"""Generate semantic token information for Robot Framework tokens.
|
|
1199
1502
|
|
|
@@ -1205,6 +1508,7 @@ class SemanticTokenGenerator:
|
|
|
1205
1508
|
col_offset: Optional column offset override
|
|
1206
1509
|
length: Optional length override
|
|
1207
1510
|
yield_arguments: Whether to yield argument tokens
|
|
1511
|
+
site: Where a variable token stands
|
|
1208
1512
|
|
|
1209
1513
|
Yields:
|
|
1210
1514
|
SemTokenInfo: Semantic token information for LSP client
|
|
@@ -1393,37 +1697,8 @@ class SemanticTokenGenerator:
|
|
|
1393
1697
|
col_offset,
|
|
1394
1698
|
length,
|
|
1395
1699
|
)
|
|
1396
|
-
elif token.type
|
|
1397
|
-
|
|
1398
|
-
cached_isinstance(node, ExceptHeader) or cached_isinstance(node, WhileHeader)
|
|
1399
|
-
) and "=" in token.value:
|
|
1400
|
-
if col_offset is None:
|
|
1401
|
-
col_offset = token.col_offset
|
|
1402
|
-
|
|
1403
|
-
name, value = token.value.split("=", 1)
|
|
1404
|
-
yield SemTokenInfo.from_token(
|
|
1405
|
-
token,
|
|
1406
|
-
RobotSemTokenTypes.VARIABLE,
|
|
1407
|
-
sem_mod,
|
|
1408
|
-
col_offset,
|
|
1409
|
-
len(name),
|
|
1410
|
-
)
|
|
1411
|
-
yield SemTokenInfo.from_token(
|
|
1412
|
-
token,
|
|
1413
|
-
SemanticTokenTypes.OPERATOR,
|
|
1414
|
-
sem_mod,
|
|
1415
|
-
col_offset + len(name),
|
|
1416
|
-
1,
|
|
1417
|
-
)
|
|
1418
|
-
yield SemTokenInfo.from_token(
|
|
1419
|
-
token,
|
|
1420
|
-
sem_type,
|
|
1421
|
-
sem_mod,
|
|
1422
|
-
col_offset + len(name) + 1,
|
|
1423
|
-
len(value),
|
|
1424
|
-
)
|
|
1425
|
-
else:
|
|
1426
|
-
yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset, length)
|
|
1700
|
+
elif token.type in (Token.VARIABLE, Token.ASSIGN):
|
|
1701
|
+
yield from self._generate_variable_sem_tokens(namespace, token, sem_type, sem_mod, site)
|
|
1427
1702
|
elif (
|
|
1428
1703
|
token.type in Token.SETTING_TOKENS and token.value and token.value[0] == "[" and token.value[-1] == "]"
|
|
1429
1704
|
):
|
|
@@ -1471,33 +1746,37 @@ class SemanticTokenGenerator:
|
|
|
1471
1746
|
if token.type in {Token.ARGUMENT, Token.TESTCASE_NAME, Token.KEYWORD_NAME} or (
|
|
1472
1747
|
token.type == Token.NAME and cached_isinstance(node, VariablesImport, LibraryImport, ResourceImport)
|
|
1473
1748
|
):
|
|
1474
|
-
if (
|
|
1749
|
+
if cached_isinstance(node, Arguments):
|
|
1750
|
+
# The name of an argument is a parameter; a default value continues as argument.
|
|
1751
|
+
name, value = split_from_equals(token.value)
|
|
1752
|
+
if name:
|
|
1753
|
+
yield from self._generate_variable_sem_tokens(
|
|
1754
|
+
namespace,
|
|
1755
|
+
Token(Token.VARIABLE, name, token.lineno, token.col_offset),
|
|
1756
|
+
SemanticTokenTypes.PARAMETER,
|
|
1757
|
+
None,
|
|
1758
|
+
VariableSite.DECLARATION,
|
|
1759
|
+
)
|
|
1760
|
+
token = Token(
|
|
1761
|
+
token.type,
|
|
1762
|
+
value or "",
|
|
1763
|
+
token.lineno,
|
|
1764
|
+
token.col_offset + len(name) + (1 if value is not None else 0),
|
|
1765
|
+
token.error,
|
|
1766
|
+
)
|
|
1767
|
+
elif (
|
|
1475
1768
|
cached_isinstance(node, Variable) and token.type == Token.ARGUMENT and node.name and node.name[0] == "&"
|
|
1476
|
-
)
|
|
1769
|
+
):
|
|
1477
1770
|
name, value = split_from_equals(token.value)
|
|
1478
1771
|
if value is not None:
|
|
1479
1772
|
length = len(name)
|
|
1480
1773
|
|
|
1481
1774
|
yield SemTokenInfo.from_token(
|
|
1482
|
-
Token(
|
|
1483
|
-
|
|
1484
|
-
name,
|
|
1485
|
-
token.lineno,
|
|
1486
|
-
token.col_offset,
|
|
1487
|
-
),
|
|
1488
|
-
(
|
|
1489
|
-
RobotSemTokenTypes.NAMED_ARGUMENT
|
|
1490
|
-
if cached_isinstance(node, Variable)
|
|
1491
|
-
else SemanticTokenTypes.PARAMETER
|
|
1492
|
-
),
|
|
1775
|
+
Token(ROBOT_NAMED_ARGUMENT, name, token.lineno, token.col_offset),
|
|
1776
|
+
RobotSemTokenTypes.NAMED_ARGUMENT,
|
|
1493
1777
|
)
|
|
1494
1778
|
yield SemTokenInfo.from_token(
|
|
1495
|
-
Token(
|
|
1496
|
-
ROBOT_OPERATOR,
|
|
1497
|
-
"=",
|
|
1498
|
-
token.lineno,
|
|
1499
|
-
token.col_offset + length,
|
|
1500
|
-
),
|
|
1779
|
+
Token(ROBOT_OPERATOR, "=", token.lineno, token.col_offset + length),
|
|
1501
1780
|
SemanticTokenTypes.OPERATOR,
|
|
1502
1781
|
)
|
|
1503
1782
|
token = Token(
|
|
@@ -1507,36 +1786,90 @@ class SemanticTokenGenerator:
|
|
|
1507
1786
|
token.col_offset + length + 1,
|
|
1508
1787
|
token.error,
|
|
1509
1788
|
)
|
|
1510
|
-
elif cached_isinstance(node, Arguments) and name:
|
|
1511
|
-
yield SemTokenInfo.from_token(
|
|
1512
|
-
Token(
|
|
1513
|
-
ROBOT_NAMED_ARGUMENT,
|
|
1514
|
-
name,
|
|
1515
|
-
token.lineno,
|
|
1516
|
-
token.col_offset,
|
|
1517
|
-
),
|
|
1518
|
-
RobotSemTokenTypes.NAMED_ARGUMENT,
|
|
1519
|
-
)
|
|
1520
|
-
token = Token(
|
|
1521
|
-
token.type,
|
|
1522
|
-
"",
|
|
1523
|
-
token.lineno,
|
|
1524
|
-
token.col_offset + len(name),
|
|
1525
|
-
token.error,
|
|
1526
|
-
)
|
|
1527
1789
|
|
|
1790
|
+
site = VariableSite.KEYWORD_NAME if token.type == Token.KEYWORD_NAME else VariableSite.USAGE
|
|
1528
1791
|
for sub_token in ModelHelper.tokenize_variables(
|
|
1529
1792
|
token,
|
|
1530
1793
|
ignore_errors=True,
|
|
1531
1794
|
identifiers="$" if token.type == Token.KEYWORD_NAME else "$@&%",
|
|
1532
1795
|
):
|
|
1533
|
-
for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, sub_token, node):
|
|
1796
|
+
for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, sub_token, node, site=site):
|
|
1534
1797
|
yield e
|
|
1535
1798
|
|
|
1536
1799
|
else:
|
|
1537
|
-
|
|
1800
|
+
# A whole VARIABLE or ASSIGN cell defines a variable.
|
|
1801
|
+
site = (
|
|
1802
|
+
VariableSite.UNTYPED_DECLARATION if cached_isinstance(node, ExceptHeader) else VariableSite.DECLARATION
|
|
1803
|
+
)
|
|
1804
|
+
for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, token, node, site=site):
|
|
1538
1805
|
yield e
|
|
1539
1806
|
|
|
1807
|
+
def _generate_variable_sem_tokens(
|
|
1808
|
+
self,
|
|
1809
|
+
namespace: Namespace,
|
|
1810
|
+
token: Token,
|
|
1811
|
+
sem_type: AnyTokenType,
|
|
1812
|
+
sem_mod: Optional[Set[AnyTokenModifier]],
|
|
1813
|
+
site: VariableSite,
|
|
1814
|
+
) -> Iterator[SemTokenInfo]:
|
|
1815
|
+
"""A variable sends its name with `sem_type` and its type hint as `type`;
|
|
1816
|
+
prefix, braces, item access, `=`, patterns, defaults and inline Python
|
|
1817
|
+
are left to the grammar. The parts are split like the semantic model
|
|
1818
|
+
splits them."""
|
|
1819
|
+
position = Position(line=token.lineno - 1, character=token.col_offset)
|
|
1820
|
+
|
|
1821
|
+
def is_defined(name: str) -> bool:
|
|
1822
|
+
return namespace.find_variable(name, position=position) is not None
|
|
1823
|
+
|
|
1824
|
+
sub_tokens = build_variable_sub_tokens(
|
|
1825
|
+
token.value, token.lineno, token.col_offset, site=site, is_defined=is_defined
|
|
1826
|
+
)
|
|
1827
|
+
if not sub_tokens:
|
|
1828
|
+
yield SemTokenInfo.from_token(token, sem_type, sem_mod)
|
|
1829
|
+
return
|
|
1830
|
+
yield from self._iter_variable_name_sem_tokens(sub_tokens, sem_type, sem_mod, self._argument_uses(namespace))
|
|
1831
|
+
|
|
1832
|
+
def _argument_uses(self, namespace: Namespace) -> FrozenSet[Tuple[int, int]]:
|
|
1833
|
+
"""Where the analysis resolved a name to an argument from `[Arguments]`, the same resolution Find References
|
|
1834
|
+
and Rename use; embedded arguments are left out."""
|
|
1835
|
+
uses = self._argument_uses_cache.get(namespace)
|
|
1836
|
+
if uses is None:
|
|
1837
|
+
uses = frozenset(
|
|
1838
|
+
(location.range.start.line, location.range.start.character)
|
|
1839
|
+
for var, locations in namespace.variable_references.items()
|
|
1840
|
+
if isinstance(var, ArgumentDefinition) and not isinstance(var, EmbeddedArgumentDefinition)
|
|
1841
|
+
for location in locations
|
|
1842
|
+
)
|
|
1843
|
+
self._argument_uses_cache[namespace] = uses
|
|
1844
|
+
return uses
|
|
1845
|
+
|
|
1846
|
+
def _iter_variable_name_sem_tokens(
|
|
1847
|
+
self,
|
|
1848
|
+
sub_tokens: Sequence[SemanticToken],
|
|
1849
|
+
sem_type: AnyTokenType,
|
|
1850
|
+
sem_mod: Optional[Set[AnyTokenModifier]],
|
|
1851
|
+
argument_uses: FrozenSet[Tuple[int, int]],
|
|
1852
|
+
) -> Iterator[SemTokenInfo]:
|
|
1853
|
+
for sub in sub_tokens:
|
|
1854
|
+
if sub.kind is TokenKind.VARIABLE:
|
|
1855
|
+
if sub.sub_tokens:
|
|
1856
|
+
yield from self._iter_variable_name_sem_tokens(
|
|
1857
|
+
sub.sub_tokens, RobotSemTokenTypes.VARIABLE, None, argument_uses
|
|
1858
|
+
)
|
|
1859
|
+
else:
|
|
1860
|
+
yield SemTokenInfo(sub.line, sub.col_offset, sub.length, RobotSemTokenTypes.VARIABLE)
|
|
1861
|
+
elif sub.kind is TokenKind.PYTHON_EXPRESSION:
|
|
1862
|
+
continue
|
|
1863
|
+
elif sub.sub_tokens:
|
|
1864
|
+
yield from self._iter_variable_name_sem_tokens(sub.sub_tokens, sem_type, sem_mod, argument_uses)
|
|
1865
|
+
elif sub.kind is TokenKind.VARIABLE_BASE:
|
|
1866
|
+
# a use of an argument is a parameter, like its declaration
|
|
1867
|
+
is_argument = (sub.range.start.line, sub.range.start.character) in argument_uses
|
|
1868
|
+
base_type = SemanticTokenTypes.PARAMETER if is_argument else sem_type
|
|
1869
|
+
yield SemTokenInfo(sub.line, sub.col_offset, sub.length, base_type, sem_mod)
|
|
1870
|
+
elif sub.kind is TokenKind.VARIABLE_TYPE_HINT:
|
|
1871
|
+
yield SemTokenInfo(sub.line, sub.col_offset, sub.length, SemanticTokenTypes.TYPE)
|
|
1872
|
+
|
|
1540
1873
|
def collect_tokens(
|
|
1541
1874
|
self,
|
|
1542
1875
|
document: TextDocument,
|