Skip to content

Commit 8936bfb

Browse files
committed
fix(vscode): some corrections in environment variable highlightning
1 parent 8cc3bd5 commit 8936bfb

File tree

6 files changed

+132
-44
lines changed

6 files changed

+132
-44
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ put this to the `settings.json`
214214
}
215215
},
216216
{
217-
"scope": "variable.other.readwrite.robotframework",
217+
"scope": "variable.name.readwrite.robotframework",
218218
"settings": {
219219
//"fontStyle": "italic",
220220
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
}
152152
},
153153
{
154-
"scope": "variable.other.readwrite.robotframework",
154+
"scope": "variable.name.readwrite.robotframework",
155155
"settings": {}
156156
},
157157
{
@@ -266,7 +266,7 @@
266266
"string.unquoted.embeddedArgument.robotframework"
267267
],
268268
"variable": [
269-
"variable.other.readwrite.robotframework"
269+
"variable.name.readwrite.robotframework"
270270
],
271271
"variableExpression": [
272272
"variable.expression.readwrite.robotframework"

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ def get_reserved_keywords() -> List[str]:
254254
global __reserved_keywords
255255

256256
if __reserved_keywords is None:
257-
__reserved_keywords = ["FOR", "END", "IF", "ELSE", "ELIF", "ELSE IF"]
257+
__reserved_keywords = ["FOR", "END", "IF", "ELSE", "ELSE IF"]
258+
258259
if get_robot_version() >= (5, 0):
259260
__reserved_keywords += [
260261
"TRY",
@@ -267,6 +268,8 @@ def get_reserved_keywords() -> List[str]:
267268
]
268269
if get_robot_version() >= (7, 0):
269270
__reserved_keywords += ["VAR"]
271+
else:
272+
__reserved_keywords += ["ELIF"]
270273

271274
__reserved_keywords = sorted(__reserved_keywords)
272275
return __reserved_keywords

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ def generate_sem_sub_tokens(
348348
if isinstance(node, (Documentation, Metadata)):
349349
sem_mod = {SemanticTokenModifiers.DOCUMENTATION}
350350

351-
if token.type == Token.VARIABLE:
351+
if token.type in [Token.VARIABLE, Token.ASSIGN]:
352352
if is_variable(token.value, "$@&%"):
353353
if col_offset is None:
354354
col_offset = token.col_offset

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

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
KeywordError,
100100
KeywordMatcher,
101101
LibraryDoc,
102+
resolve_robot_variables,
102103
)
103104

104105
EXTRACT_COMMENT_PATTERN = re.compile(r".*(?:^ *|\t+| {2,})#(?P<comment>.*)$")
@@ -184,22 +185,19 @@ def visit_Variable(self, node: Statement) -> None: # noqa: N802
184185
class BlockVariableVisitor(Visitor):
185186
def __init__(
186187
self,
187-
library_doc: LibraryDoc,
188-
global_variables: List[VariableDefinition],
189-
source: str,
188+
namespace: "Namespace",
189+
nodes: Optional[List[ast.AST]] = None,
190190
position: Optional[Position] = None,
191191
in_args: bool = True,
192192
) -> None:
193193
super().__init__()
194-
self.library_doc = library_doc
195-
self.global_variables = global_variables
196-
self.source = source
194+
self.namespace = namespace
195+
self.nodes = nodes
197196
self.position = position
198197
self.in_args = in_args
199198

200199
self._results: Dict[str, VariableDefinition] = {}
201200
self.current_kw_doc: Optional[KeywordDoc] = None
202-
self._var_statements_vars: List[VariableDefinition] = []
203201

204202
def get(self, model: ast.AST) -> List[VariableDefinition]:
205203
self._results = {}
@@ -224,7 +222,7 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802
224222
name_token = node.get_token(Token.KEYWORD_NAME)
225223

226224
if name_token is not None and name_token.value:
227-
keyword = ModelHelper.get_keyword_definition_at_token(self.library_doc, name_token)
225+
keyword = ModelHelper.get_keyword_definition_at_token(self.namespace.get_library_doc(), name_token)
228226
self.current_kw_doc = keyword
229227

230228
for variable_token in filter(
@@ -246,7 +244,7 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802
246244
col_offset=variable_token.col_offset,
247245
end_line_no=variable_token.lineno,
248246
end_col_offset=variable_token.end_col_offset,
249-
source=self.source,
247+
source=self.namespace.source,
250248
keyword_doc=self.current_kw_doc,
251249
)
252250

@@ -290,7 +288,7 @@ def visit_Arguments(self, node: Statement) -> None: # noqa: N802
290288
col_offset=argument.col_offset,
291289
end_line_no=argument.lineno,
292290
end_col_offset=argument.end_col_offset,
293-
source=self.source,
291+
source=self.namespace.source,
294292
keyword_doc=self.current_kw_doc,
295293
)
296294
self._results[argument.value] = arg_def
@@ -312,12 +310,48 @@ def visit_ExceptHeader(self, node: Statement) -> None: # noqa: N802
312310
col_offset=variable.col_offset,
313311
end_line_no=variable.lineno,
314312
end_col_offset=variable.end_col_offset,
315-
source=self.source,
313+
source=self.namespace.source,
316314
)
317315

318316
except VariableError:
319317
pass
320318

319+
def _get_var_name(self, original: str, position: Position, require_assign: bool = True) -> Optional[str]:
320+
robot_variables = resolve_robot_variables(
321+
str(self.namespace.imports_manager.root_folder),
322+
str(Path(self.namespace.source).parent) if self.namespace.source else ".",
323+
self.namespace.imports_manager.get_resolvable_command_line_variables(),
324+
variables=self.namespace.get_resolvable_variables(),
325+
)
326+
327+
try:
328+
replaced = robot_variables.replace_string(original)
329+
except VariableError:
330+
replaced = original
331+
try:
332+
name = self._resolve_var_name(replaced, robot_variables)
333+
except ValueError:
334+
name = original
335+
match = search_variable(name, identifiers="$@&")
336+
match.resolve_base(robot_variables)
337+
valid = match.is_assign() if require_assign else match.is_variable()
338+
if not valid:
339+
return None
340+
return str(match)
341+
342+
def _resolve_var_name(self, name: str, variables: Any) -> str:
343+
if name.startswith("\\"):
344+
name = name[1:]
345+
if len(name) < 2 or name[0] not in "$@&":
346+
raise ValueError
347+
if name[1] != "{":
348+
name = f"{name[0]}{{{name[1:]}}}"
349+
match = search_variable(name, identifiers="$@&", ignore_errors=True)
350+
match.resolve_base(variables)
351+
if not match.is_assign():
352+
raise ValueError
353+
return str(match)
354+
321355
def visit_KeywordCall(self, node: Statement) -> None: # noqa: N802
322356
# TODO analyze "Set Local/Global/Suite Variable"
323357

@@ -341,12 +375,65 @@ def visit_KeywordCall(self, node: Statement) -> None: # noqa: N802
341375
col_offset=variable_token.col_offset,
342376
end_line_no=variable_token.lineno,
343377
end_col_offset=variable_token.end_col_offset,
344-
source=self.source,
378+
source=self.namespace.source,
345379
)
346380

347381
except VariableError:
348382
pass
349383

384+
keyword_token = node.get_token(Token.KEYWORD)
385+
if keyword_token is None or not keyword_token.value:
386+
return
387+
388+
keyword = self.namespace.find_keyword(keyword_token.value, raise_keyword_error=False)
389+
if keyword is None:
390+
return
391+
392+
if keyword.libtype == "LIBRARY" and keyword.libname == "BuiltIn":
393+
var_type = None
394+
if keyword.name == "Set Suite Variable":
395+
var_type = VariableDefinition
396+
elif keyword.name == "Set Global Variable":
397+
var_type = GlobalVariableDefinition
398+
elif keyword.name == "Set Test Variable" or keyword.name == "Set Task Variable":
399+
var_type = TestVariableDefinition
400+
elif keyword.name == "Set Local Variable":
401+
var_type = LocalVariableDefinition
402+
else:
403+
return
404+
try:
405+
variable = node.get_token(Token.ARGUMENT)
406+
if variable is None:
407+
return
408+
409+
position = range_from_node(node).start
410+
position.character = 0
411+
var_name = self._get_var_name(variable.value, position)
412+
413+
if var_name is None or not is_variable(var_name):
414+
return
415+
416+
var = var_type(
417+
name=var_name,
418+
name_token=strip_variable_token(variable),
419+
line_no=variable.lineno,
420+
col_offset=variable.col_offset,
421+
end_line_no=variable.lineno,
422+
end_col_offset=variable.end_col_offset,
423+
source=self.namespace.source,
424+
)
425+
426+
if var_name not in self._results or type(self._results[var_name]) != type(var):
427+
if isinstance(var, LocalVariableDefinition) or not any(
428+
l for l in self.namespace.get_global_variables() if l.matcher == var.matcher
429+
):
430+
self._results[var_name] = var
431+
else:
432+
self._results.pop(var_name, None)
433+
434+
except VariableError:
435+
pass
436+
350437
def visit_InlineIfHeader(self, node: Statement) -> None: # noqa: N802
351438
for assign_token in node.get_tokens(Token.ASSIGN):
352439
variable_token = self.get_variable_token(assign_token)
@@ -368,7 +455,7 @@ def visit_InlineIfHeader(self, node: Statement) -> None: # noqa: N802
368455
col_offset=variable_token.col_offset,
369456
end_line_no=variable_token.lineno,
370457
end_col_offset=variable_token.end_col_offset,
371-
source=self.source,
458+
source=self.namespace.source,
372459
)
373460

374461
except VariableError:
@@ -386,7 +473,7 @@ def visit_ForHeader(self, node: Statement) -> None: # noqa: N802
386473
col_offset=variable_token.col_offset,
387474
end_line_no=variable_token.lineno,
388475
end_col_offset=variable_token.end_col_offset,
389-
source=self.source,
476+
source=self.namespace.source,
390477
)
391478

392479
def visit_Var(self, node: Statement) -> None: # noqa: N802
@@ -421,14 +508,12 @@ def visit_Var(self, node: Statement) -> None: # noqa: N802
421508
col_offset=variable.col_offset,
422509
end_line_no=variable.lineno,
423510
end_col_offset=variable.end_col_offset,
424-
source=self.source,
511+
source=self.namespace.source,
425512
)
426513

427-
self._var_statements_vars.append(var)
428-
429514
if var_name not in self._results or type(self._results[var_name]) != type(var):
430515
if isinstance(var, LocalVariableDefinition) or not any(
431-
l for l in self.global_variables if l.matcher == var.matcher
516+
l for l in self.namespace.get_global_variables() if l.matcher == var.matcher
432517
):
433518
self._results[var_name] = var
434519
else:
@@ -924,9 +1009,8 @@ def yield_variables(
9241009
(
9251010
(
9261011
BlockVariableVisitor(
927-
self.get_library_doc(),
928-
self.get_global_variables(),
929-
self.source,
1012+
self,
1013+
nodes,
9301014
position,
9311015
isinstance(test_or_keyword_nodes[-1], Arguments) if nodes else False,
9321016
).get(test_or_keyword)

syntaxes/robotframework.tmLanguage.json

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
"variable_setting": {
99
"name": "meta.variable.assign.robotframework",
1010
"contentName": "string.unquoted.argument.robotframework",
11-
"begin": "^(?:([$@&%]\\{)(.*?}*)(\\})( ?=)?)",
11+
"begin": "^(?:([$@&]\\{)(.*?}*)(\\})( ?=)?)",
1212
"beginCaptures": {
13-
"1": { "name": "unctuation.definition.variable.begin.robotframework" },
14-
"2": { "name": "variable.other.readwrite.robotframework" },
15-
"3": { "name": "unctuation.definition.variable.end.robotframework" },
13+
"1": { "name": "punctuation.definition.variable.begin.robotframework" },
14+
"2": { "name": "variable.name.readwrite.robotframework" },
15+
"3": { "name": "punctuation.definition.variable.end.robotframework" },
1616
"4": { "name": "keyword.operator.robotframework" }
1717
},
1818
"end": "^(?!\\s*\\.\\.\\.)",
@@ -42,7 +42,7 @@
4242
},
4343
"simple_var": {
4444
"name": "meta.variables.robotframework",
45-
"contentName": "variable.other.readwrite.robotframework",
45+
"contentName": "variable.name.readwrite.robotframework",
4646
"begin": "[$@&]\\{",
4747
"end": "(\\})|(?= {2}| ?\\t| ?$)",
4848
"beginCaptures": { "0": { "name": "punctuation.definition.variable.begin.robotframework" } },
@@ -51,23 +51,24 @@
5151
},
5252
"env_var": {
5353
"name": "meta.variables.robotframework",
54-
"contentName": "variable.other.readwrite.robotframework",
55-
"begin": "%\\{",
54+
"begin": "(%\\{)",
5655
"end": "(\\})|(?= {2}| ?\\t| ?$)",
5756
"beginCaptures": { "0": { "name": "punctuation.definition.envvar.begin.robotframework" } },
5857
"endCaptures": { "1": { "name": "punctuation.definition.envvar.end.robotframework" } },
5958
"patterns": [ { "include": "#env_key_value_pair" } ]
6059
},
6160
"env_key_value_pair": {
62-
"match": "([^=]*)((=)(.*))?",
63-
"captures": {
64-
"1": { "patterns": [ { "include": "#variables" } ] },
65-
"3": { "name": "keyword.operator.robotframework" },
66-
"4": {
67-
"name": "string.unquoted.value.robotframework",
61+
"begin": "([^=\\}]*)(=)?",
62+
"end": "(?=\\}| {2}| ?\\t| ?$)",
63+
"contentName": "constant.character.robotframework",
64+
"beginCaptures": {
65+
"1": {
66+
"name": "variable.name.readwrite.robotframework",
6867
"patterns": [ { "include": "#variables" } ]
69-
}
70-
}
68+
},
69+
"2": { "name": "keyword.operator.robotframework" }
70+
},
71+
"patterns": [ { "include": "#variables" } ]
7172
},
7273
"comment": {
7374
"name": "comment.robotframework",
@@ -123,7 +124,7 @@
123124
"begin": "(?<=\\s)(?=\\s*(?:(?=[$@&])))((?:\\s)*(?:[$&@]{(?:.*?)}(?:\\[.*?\\])?(?: ?=?\\s*))*)(.*?)(?: {2,}| ?\\t ?|$)",
124125
"beginCaptures": {
125126
"1": {
126-
"name": "variable.other.readwrite.robotframework",
127+
"name": "variable.name.readwrite.robotframework",
127128
"patterns": [ { "include": "#variable_assignment_from_kw" } ]
128129
},
129130
"2": { "name": "entity.name.function.keyword-call.robotframework" }
@@ -146,7 +147,7 @@
146147
]
147148
},
148149
"line_continuation": {
149-
"match": "^(\\s*\\.\\.\\.)(?: {2,}| ?\\t+| ?$)(?!ELSE)",
150+
"match": "^(\\s*\\.\\.\\.)(?: {2,}| ?\\t+| ?$)",
150151
"name": "keyword.operator.robotframework"
151152
},
152153
"impossible": {
@@ -877,7 +878,7 @@
877878
"match": "(\\$)([[:alpha:]_]\\w*)",
878879
"captures": {
879880
"1": { "name": "punctuation.definition.variable.begin.robotframework" },
880-
"2": { "name": "variable.other.readwrite.robotframework" }
881+
"2": { "name": "variable.name.readwrite.robotframework" }
881882
}
882883
},
883884
"ellipsis": {

0 commit comments

Comments
 (0)