Skip to content

Commit 5b41e43

Browse files
committed
correct handling of variable assign with =
1 parent feea3b4 commit 5b41e43

File tree

31 files changed

+120
-66
lines changed

31 files changed

+120
-66
lines changed

robotcode/language_server/robotframework/diagnostics/entities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class VariableDefinition(SourceEntity):
9696
value: Any = None
9797

9898
def __hash__(self) -> int:
99-
return hash((type(self), self.name, self.type, self.range, self.source))
99+
return hash((type(self), self.name, self.type, self.range, self.source, self.name_token))
100100

101101
def range(self) -> Range:
102102
return Range(

robotcode/language_server/robotframework/diagnostics/namespace.py

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,20 @@ async def visit_Variable(self, node: ast.AST) -> None: # noqa: N802
9797
from robot.variables import is_variable
9898

9999
n = cast(Variable, node)
100-
name = n.get_token(Token.VARIABLE)
100+
name = next(
101+
tokenize_variables(n.get_token(Token.VARIABLE), "$@&", ignore_errors=True, extra_types={Token.VARIABLE}),
102+
None,
103+
)
101104

102105
if name is not None and is_variable(name.value):
103106
self._results.append(
104107
VariableDefinition(
105108
name=n.name,
106109
name_token=name,
107-
line_no=name.lineno,
108-
col_offset=name.col_offset,
109-
end_line_no=name.lineno,
110-
end_col_offset=name.end_col_offset,
110+
line_no=n.lineno,
111+
col_offset=n.col_offset,
112+
end_line_no=n.lineno,
113+
end_col_offset=n.end_col_offset,
111114
source=self.source,
112115
has_value=bool(n.value),
113116
resolvable=True,
@@ -154,10 +157,10 @@ async def visit_KeywordName(self, node: ast.AST) -> None: # noqa: N802
154157
self._results[name] = ArgumentDefinition(
155158
name=name,
156159
name_token=variable_token,
157-
line_no=variable_token.lineno,
158-
col_offset=variable_token.col_offset,
159-
end_line_no=variable_token.lineno,
160-
end_col_offset=variable_token.end_col_offset,
160+
line_no=n.lineno,
161+
col_offset=n.col_offset,
162+
end_line_no=n.lineno,
163+
end_col_offset=n.end_col_offset,
161164
source=self.source,
162165
)
163166

@@ -191,10 +194,10 @@ async def visit_Arguments(self, node: ast.AST) -> None: # noqa: N802
191194
self._results[argument.value] = ArgumentDefinition(
192195
name=argument.value,
193196
name_token=argument,
194-
line_no=argument.lineno,
195-
col_offset=argument.col_offset,
196-
end_line_no=argument.lineno,
197-
end_col_offset=argument.end_col_offset,
197+
line_no=n.lineno,
198+
col_offset=n.col_offset,
199+
end_line_no=n.lineno,
200+
end_col_offset=n.end_col_offset,
198201
source=self.source,
199202
)
200203

@@ -217,10 +220,10 @@ async def visit_ExceptHeader(self, node: ast.AST) -> None: # noqa: N802
217220
self._results[variable.value] = LocalVariableDefinition(
218221
name=variable.value,
219222
name_token=variable,
220-
line_no=variable.lineno,
221-
col_offset=variable.col_offset,
222-
end_line_no=variable.lineno,
223-
end_col_offset=variable.end_col_offset,
223+
line_no=n.lineno,
224+
col_offset=n.col_offset,
225+
end_line_no=n.lineno,
226+
end_col_offset=n.end_col_offset,
224227
source=self.source,
225228
)
226229

@@ -252,10 +255,10 @@ async def visit_KeywordCall(self, node: ast.AST) -> None: # noqa: N802
252255
self._results[variable_token.value] = LocalVariableDefinition(
253256
name=variable_token.value,
254257
name_token=variable_token,
255-
line_no=variable_token.lineno,
256-
col_offset=variable_token.col_offset,
257-
end_line_no=variable_token.lineno,
258-
end_col_offset=variable_token.end_col_offset,
258+
line_no=n.lineno,
259+
col_offset=n.col_offset,
260+
end_line_no=n.lineno,
261+
end_col_offset=n.end_col_offset,
259262
source=self.source,
260263
)
261264

@@ -274,10 +277,10 @@ async def visit_ForHeader(self, node: ast.AST) -> None: # noqa: N802
274277
self._results[variable_token.value] = LocalVariableDefinition(
275278
name=variable_token.value,
276279
name_token=variable_token,
277-
line_no=node.lineno,
278-
col_offset=node.col_offset,
279-
end_line_no=variable_token.lineno,
280-
end_col_offset=variable_token.end_col_offset,
280+
line_no=n.lineno,
281+
col_offset=n.col_offset,
282+
end_line_no=n.lineno,
283+
end_col_offset=n.end_col_offset,
281284
source=self.source,
282285
)
283286

@@ -666,7 +669,10 @@ def get_command_line_variables(self) -> List[VariableDefinition]:
666669

667670
@_logger.call
668671
async def yield_variables(
669-
self, nodes: Optional[List[ast.AST]] = None, position: Optional[Position] = None
672+
self,
673+
nodes: Optional[List[ast.AST]] = None,
674+
position: Optional[Position] = None,
675+
skip_commandline_variables: bool = False,
670676
) -> AsyncGenerator[Tuple[VariableMatcher, VariableDefinition], None]:
671677
from robot.parsing.model.blocks import Keyword, TestCase
672678

@@ -680,7 +686,7 @@ async def yield_variables(
680686
for n in nodes or []
681687
if isinstance(n, (Keyword, TestCase))
682688
],
683-
(e for e in self.get_command_line_variables()),
689+
[] if skip_commandline_variables else (e for e in self.get_command_line_variables()),
684690
(e for e in await self.get_own_variables()),
685691
*(e.variables for e in self._resources.values()),
686692
*(e.variables for e in self._variables.values()),
@@ -723,7 +729,11 @@ async def get_variable_matchers(
723729

724730
@_logger.call
725731
async def find_variable(
726-
self, name: str, nodes: Optional[List[ast.AST]], position: Optional[Position] = None
732+
self,
733+
name: str,
734+
nodes: Optional[List[ast.AST]],
735+
position: Optional[Position] = None,
736+
skip_commandline_variables: bool = False,
727737
) -> Optional[VariableDefinition]:
728738

729739
await self.ensure_initialized()
@@ -732,7 +742,11 @@ async def find_variable(
732742

733743
matcher = VariableMatcher(name)
734744

735-
async for m, v in self.yield_variables(nodes, position):
745+
async for m, v in self.yield_variables(
746+
nodes,
747+
position,
748+
skip_commandline_variables=skip_commandline_variables,
749+
):
736750
if matcher == m:
737751
return v
738752

@@ -742,7 +756,11 @@ async def find_variable(
742756
name = f"{name[0]}{{{base_name}}}"
743757

744758
matcher = VariableMatcher(name)
745-
async for m, v in self.yield_variables(nodes, position):
759+
async for m, v in self.yield_variables(
760+
nodes,
761+
position,
762+
skip_commandline_variables=skip_commandline_variables,
763+
):
746764
if matcher == m:
747765
return v
748766

robotcode/language_server/robotframework/parts/document_highlight.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,18 @@ async def _highlight_default(
113113
for token in tokens:
114114
try:
115115
for sub_token in filter(
116-
lambda s: s.type == RobotToken.VARIABLE, tokenize_variables(token, ignore_errors=True)
116+
lambda s: s.type == RobotToken.VARIABLE,
117+
tokenize_variables(token, ignore_errors=True, extra_types={RobotToken.VARIABLE}),
117118
):
118119
range = range_from_token(sub_token)
119120

120121
if position.is_in_range(range):
121-
variable = await namespace.find_variable(sub_token.value, nodes, position)
122+
variable = await namespace.find_variable(
123+
sub_token.value,
124+
nodes,
125+
position,
126+
skip_commandline_variables=True,
127+
)
122128
if variable is not None:
123129
return [
124130
DocumentHighlight(e.range, DocumentHighlightKind.TEXT)

robotcode/language_server/robotframework/parts/goto.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ async def _definition_default(
117117
self, nodes: List[ast.AST], document: TextDocument, position: Position, collect_type: CollectType
118118
) -> Union[Location, List[Location], List[LocationLink], None]:
119119
from robot.api.parsing import Token as RobotToken
120+
from robot.parsing.model.statements import Variable
120121

121122
namespace = await self.parent.documents_cache.get_namespace(document)
122123
if namespace is None:
@@ -135,12 +136,20 @@ async def _definition_default(
135136
for token in tokens:
136137
try:
137138
for sub_token in filter(
138-
lambda s: s.type == RobotToken.VARIABLE, tokenize_variables(token, ignore_errors=True)
139+
lambda s: s.type == RobotToken.VARIABLE,
140+
tokenize_variables(token, ignore_errors=True, extra_types={RobotToken.VARIABLE}),
139141
):
140142
range = range_from_token(sub_token)
141143

142144
if position.is_in_range(range):
143-
variable = await namespace.find_variable(sub_token.value, nodes, position)
145+
variable = await namespace.find_variable(
146+
sub_token.value,
147+
nodes,
148+
position,
149+
collect_type == CollectType.DEFINITION
150+
or isinstance(node, Variable)
151+
and token.type == RobotToken.VARIABLE,
152+
)
144153
if variable is not None and variable.source:
145154
return [
146155
LocationLink(

robotcode/language_server/robotframework/parts/hover.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ async def collect(
9090

9191
async def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: Position) -> Optional[Hover]:
9292
from robot.api.parsing import Token as RobotToken
93+
from robot.parsing.model.statements import Variable
9394

9495
namespace = await self.parent.documents_cache.get_namespace(document)
9596
if namespace is None:
@@ -107,12 +108,18 @@ async def _hover_default(self, nodes: List[ast.AST], document: TextDocument, pos
107108
for token in tokens:
108109
try:
109110
for sub_token in filter(
110-
lambda s: s.type == RobotToken.VARIABLE, tokenize_variables(token, ignore_errors=True)
111+
lambda s: s.type == RobotToken.VARIABLE,
112+
tokenize_variables(token, ignore_errors=True, extra_types={RobotToken.VARIABLE}),
111113
):
112114
range = range_from_token(sub_token)
113115

114116
if position.is_in_range(range):
115-
variable = await namespace.find_variable(sub_token.value, nodes, position)
117+
variable = await namespace.find_variable(
118+
sub_token.value,
119+
nodes,
120+
position,
121+
skip_commandline_variables=isinstance(node, Variable) and token.type == RobotToken.VARIABLE,
122+
)
116123
if variable is not None:
117124
if variable.has_value or variable.resolvable:
118125
try:

robotcode/language_server/robotframework/parts/references.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,27 @@ async def _references_default(
167167
for token in tokens:
168168
try:
169169
for sub_token in filter(
170-
lambda s: s.type == RobotToken.VARIABLE, tokenize_variables(token, ignore_errors=True)
170+
lambda s: s.type == RobotToken.VARIABLE,
171+
tokenize_variables(token, ignore_errors=True, extra_types={RobotToken.VARIABLE}),
171172
):
172173
range = range_from_token(sub_token)
173174

174175
if position.is_in_range(range):
175-
variable = await namespace.find_variable(sub_token.value, nodes, position)
176+
variable = await namespace.find_variable(
177+
sub_token.value,
178+
nodes,
179+
position,
180+
skip_commandline_variables=True,
181+
)
176182
if variable is not None:
177183
return [
178184
*(
179185
[
180186
Location(
181187
uri=str(Uri.from_path(variable.source)),
182-
range=variable.range(),
188+
range=range_from_token(variable.name_token)
189+
if variable.name_token
190+
else variable.range(),
183191
),
184192
]
185193
if context.include_declaration and variable.source
@@ -238,12 +246,13 @@ async def find_variable_references_in_file(
238246

239247
if isinstance(node, HasTokens):
240248
for token in node.tokens:
241-
for sub_token in tokenize_variables(token):
249+
for sub_token in tokenize_variables(token, extra_types={RobotToken.VARIABLE}):
242250
if sub_token.type == RobotToken.VARIABLE:
243251
found_variable = await namespace.find_variable(
244252
sub_token.value,
245253
[*([current_block] if current_block is not None else []), node],
246254
range_from_token(token).start,
255+
skip_commandline_variables=True,
247256
)
248257

249258
if found_variable == variable:

robotcode/language_server/robotframework/utils/ast.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,12 @@ def tokenize_variables(
212212
*(extra_types if extra_types is not None else set()),
213213
}:
214214
return _tokenize_no_variables(token)
215-
variables = VariableIterator(token.value, identifiers=identifiers, ignore_errors=ignore_errors)
215+
216+
value = token.value
217+
if token.type == RobotToken.VARIABLE and value.endswith("="):
218+
value = value[:-1].rstrip()
219+
220+
variables = VariableIterator(value, identifiers=identifiers, ignore_errors=ignore_errors)
216221
if not variables:
217222
return _tokenize_no_variables(token)
218223
return _tokenize_variables(token, variables)

tests/robotcode/language_server/robotframework/parts/test_goto/test_definition_goto_robot_017_018_Variable_.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ result:
1313
line: 17
1414
target_range:
1515
end:
16-
character: 8
16+
character: 28
1717
line: 12
1818
start:
1919
character: 0

tests/robotcode/language_server/robotframework/parts/test_goto/test_definition_goto_robot_017_021_Variable_.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ result:
1313
line: 17
1414
target_range:
1515
end:
16-
character: 8
16+
character: 28
1717
line: 12
1818
start:
1919
character: 0

tests/robotcode/language_server/robotframework/parts/test_goto/test_definition_goto_robot_017_024_Variable_.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ result:
1313
line: 17
1414
target_range:
1515
end:
16-
character: 8
16+
character: 28
1717
line: 12
1818
start:
1919
character: 0

0 commit comments

Comments
 (0)