robotcode-robot 1.2.0__py3-none-any.whl → 1.3.0__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.
@@ -1 +1 @@
1
- __version__ = "1.2.0"
1
+ __version__ = "1.3.0"
@@ -2371,11 +2371,11 @@ class RobotConfig(RobotExtendBaseProfile):
2371
2371
 
2372
2372
  Examples:
2373
2373
  ```toml
2374
- default_profiles = "default"
2374
+ default-profiles = "default"
2375
2375
  ```
2376
2376
 
2377
2377
  ```toml
2378
- default_profiles = ["default", "Firefox"]
2378
+ default-profiles = ["default", "Firefox"]
2379
2379
  ```
2380
2380
  """
2381
2381
  )
@@ -17,10 +17,9 @@ from typing_extensions import Concatenate, ParamSpec
17
17
 
18
18
  from robot.parsing.lexer.tokens import Token
19
19
  from robotcode.core.lsp.types import Position, Range
20
- from robotcode.robot.utils.match import normalize
21
20
 
22
21
  from ..utils.ast import range_from_token
23
- from ..utils.variables import search_variable
22
+ from ..utils.variables import VariableMatcher, search_variable
24
23
 
25
24
  if TYPE_CHECKING:
26
25
  from robotcode.robot.diagnostics.library_doc import KeywordDoc, LibraryDoc
@@ -164,48 +163,6 @@ class VariablesImport(Import):
164
163
  return hash((type(self), self.name, self.args))
165
164
 
166
165
 
167
- class InvalidVariableError(Exception):
168
- pass
169
-
170
-
171
- class VariableMatcher:
172
- def __init__(self, name: str) -> None:
173
- self.name = name
174
-
175
- match = search_variable(name, "$@&%", ignore_errors=True)
176
-
177
- if match.base is None:
178
- raise InvalidVariableError(f"Invalid variable '{name}'")
179
-
180
- self.base = match.base
181
-
182
- self.normalized_name = normalize(self.base)
183
-
184
- def __eq__(self, o: object) -> bool:
185
- if type(o) is VariableMatcher:
186
- return o.normalized_name == self.normalized_name
187
-
188
- if type(o) is str:
189
- match = search_variable(o, "$@&%", ignore_errors=True)
190
- base = match.base
191
- if base is None:
192
- return False
193
-
194
- normalized = normalize(base)
195
- return self.normalized_name == normalized
196
-
197
- return False
198
-
199
- def __hash__(self) -> int:
200
- return hash(self.normalized_name)
201
-
202
- def __str__(self) -> str:
203
- return self.name
204
-
205
- def __repr__(self) -> str:
206
- return f"{type(self).__name__}(name={self.name!r})"
207
-
208
-
209
166
  class VariableDefinitionType(Enum):
210
167
  VARIABLE = "suite variable"
211
168
  LOCAL_VARIABLE = "local variable"
@@ -230,10 +187,17 @@ class VariableDefinition(SourceEntity):
230
187
 
231
188
  value: Any = field(default=None, compare=False)
232
189
  value_is_native: bool = field(default=False, compare=False)
190
+ value_type: Optional[str] = field(default=None, compare=False)
233
191
 
234
192
  @functools.cached_property
235
193
  def matcher(self) -> VariableMatcher:
236
- return VariableMatcher(self.name)
194
+ return search_variable(self.name)
195
+
196
+ @functools.cached_property
197
+ def convertable_name(self) -> str:
198
+ m = self.matcher
199
+ value_type = f": {self.value_type}" if self.value_type else ""
200
+ return f"{m.identifier}{{{m.base.strip()}{value_type}}}"
237
201
 
238
202
  @single_call
239
203
  def __hash__(self) -> int:
@@ -311,6 +275,15 @@ class ArgumentDefinition(LocalVariableDefinition):
311
275
  return hash((type(self), self.name, self.type, self.range, self.source))
312
276
 
313
277
 
278
+ @dataclass
279
+ class EmbeddedArgumentDefinition(ArgumentDefinition):
280
+ pattern: Optional[str] = field(default=None, compare=False)
281
+
282
+ @single_call
283
+ def __hash__(self) -> int:
284
+ return hash((type(self), self.name, self.type, self.range, self.source))
285
+
286
+
314
287
  @dataclass
315
288
  class LibraryArgumentDefinition(ArgumentDefinition):
316
289
  @single_call
@@ -43,3 +43,4 @@ class Error:
43
43
  VARIABLE_OVERRIDDEN = "VariableOverridden"
44
44
  MODEL_ERROR = "ModelError"
45
45
  TOKEN_ERROR = "TokenError"
46
+ ASSIGN_MARK_ALLOWED_ONLY_ON_LAST_VAR = "AssignmentMarkAllowedOnlyOnLastVariable"
@@ -1527,11 +1527,11 @@ class ImportsManager:
1527
1527
  variables: Optional[Dict[str, Any]] = None,
1528
1528
  ) -> _ResourcesEntry:
1529
1529
  source = self.find_resource(name, base_dir, variables=variables)
1530
+ source_path = normalized_path(Path(source))
1530
1531
 
1531
1532
  def _get_document() -> TextDocument:
1532
- self._logger.debug(lambda: f"Load resource {name} from source {source}", context_name="import")
1533
+ self._logger.debug(lambda: f"Load resource {name} from source {source_path}", context_name="import")
1533
1534
 
1534
- source_path = normalized_path(Path(source))
1535
1535
  extension = source_path.suffix
1536
1536
  if extension.lower() not in RESOURCE_EXTENSIONS:
1537
1537
  raise ImportError(
@@ -1541,7 +1541,7 @@ class ImportsManager:
1541
1541
 
1542
1542
  return self.documents_manager.get_or_open_document(source_path)
1543
1543
 
1544
- entry_key = _ResourcesEntryKey(source)
1544
+ entry_key = _ResourcesEntryKey(str(source_path))
1545
1545
 
1546
1546
  with self._resources_lock:
1547
1547
  if entry_key not in self._resources:
@@ -87,9 +87,25 @@ class KeywordFinder:
87
87
  try:
88
88
  result = self._find_keyword(name, handle_bdd_style)
89
89
  if result is None:
90
+ error_message = "No keyword with found."
91
+
92
+ if name and name.strip(": ").upper() == "FOR":
93
+ error_message = (
94
+ f"Support for the old FOR loop syntax has been removed. "
95
+ f"Replace '{name}' with 'FOR', end the loop with 'END', and "
96
+ f"remove escaping backslashes."
97
+ )
98
+ elif name and name == "\\":
99
+ error_message = (
100
+ "No keyword with name '\\' found. If it is used inside a for "
101
+ "loop, remove escaping backslashes and end the loop with 'END'."
102
+ )
103
+ else:
104
+ error_message = f"No keyword with name '{name}' found."
105
+
90
106
  self.diagnostics.append(
91
107
  DiagnosticsEntry(
92
- f"No keyword with name '{name}' found.",
108
+ error_message,
93
109
  DiagnosticSeverity.ERROR,
94
110
  Error.KEYWORD_NOT_FOUND,
95
111
  )
@@ -306,17 +322,32 @@ class KeywordFinder:
306
322
  return True
307
323
  return False
308
324
 
309
- def _is_better_match(
310
- self,
311
- candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
312
- other: Tuple[Optional[LibraryEntry], KeywordDoc],
313
- ) -> bool:
314
- return (
315
- other[1].matcher.embedded_arguments is not None
316
- and candidate[1].matcher.embedded_arguments is not None
317
- and other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
318
- and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
319
- )
325
+ if get_robot_version() >= (7, 3):
326
+
327
+ def _is_better_match(
328
+ self,
329
+ candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
330
+ other: Tuple[Optional[LibraryEntry], KeywordDoc],
331
+ ) -> bool:
332
+ return (
333
+ other[1].matcher.embedded_arguments is not None
334
+ and candidate[1].matcher.embedded_arguments is not None
335
+ and other[1].matcher.embedded_arguments.matches(candidate[1].name)
336
+ and not candidate[1].matcher.embedded_arguments.matches(other[1].name)
337
+ )
338
+ else:
339
+
340
+ def _is_better_match(
341
+ self,
342
+ candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
343
+ other: Tuple[Optional[LibraryEntry], KeywordDoc],
344
+ ) -> bool:
345
+ return (
346
+ other[1].matcher.embedded_arguments is not None
347
+ and candidate[1].matcher.embedded_arguments is not None
348
+ and other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
349
+ and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
350
+ )
320
351
 
321
352
  @functools.cached_property
322
353
  def _resource_imports(self) -> List[ResourceEntry]:
@@ -67,7 +67,7 @@ from robotcode.core.utils.path import normalized_path
67
67
  from ..utils import get_robot_version
68
68
  from ..utils.ast import (
69
69
  cached_isinstance,
70
- get_variable_token,
70
+ get_first_variable_token,
71
71
  range_from_token,
72
72
  strip_variable_token,
73
73
  )
@@ -199,7 +199,7 @@ def convert_from_rest(text: str) -> str:
199
199
 
200
200
 
201
201
  if get_robot_version() >= (6, 0):
202
- # monkey patch robot framework
202
+ # monkey patch robot framework for performance reasons
203
203
  _old_from_name = EmbeddedArguments.from_name
204
204
 
205
205
  @functools.lru_cache(maxsize=8192)
@@ -214,8 +214,14 @@ if get_robot_version() >= (6, 0):
214
214
  except (VariableError, DataError):
215
215
  return ()
216
216
 
217
- def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
218
- return embedded_arguments.match(name) is not None
217
+ if get_robot_version() >= (7, 3):
218
+
219
+ def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
220
+ return bool(embedded_arguments.matches(name))
221
+ else:
222
+
223
+ def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
224
+ return embedded_arguments.match(name) is not None
219
225
 
220
226
  else:
221
227
 
@@ -2779,7 +2785,7 @@ def _get_argument_definitions_from_line(
2779
2785
 
2780
2786
  for argument_token in (cast(RobotToken, e) for e in arguments):
2781
2787
  try:
2782
- argument = get_variable_token(argument_token)
2788
+ argument = get_first_variable_token(argument_token)
2783
2789
 
2784
2790
  if argument is not None and argument.value != "@{}":
2785
2791
  if argument.value not in args:
@@ -253,7 +253,7 @@ class ModelHelper:
253
253
 
254
254
  return lib_entry, kw_namespace
255
255
 
256
- match_extended = re.compile(
256
+ MATCH_EXTENDED = re.compile(
257
257
  r"""
258
258
  (.+?) # base name (group 1)
259
259
  ([^\s\w].+) # extended part (group 2)
@@ -500,9 +500,9 @@ class ModelHelper:
500
500
  and sub_token.value[1:2] == "{"
501
501
  and sub_token.value[-1:] == "}"
502
502
  ):
503
- match = cls.match_extended.match(name[2:-1])
504
- if match is not None:
505
- base_name, _ = match.groups()
503
+ extended_match = cls.MATCH_EXTENDED.match(name[2:-1])
504
+ if extended_match is not None:
505
+ base_name, _ = extended_match.groups()
506
506
  name = f"{name[0]}{{{base_name.strip()}}}"
507
507
  var = namespace.find_variable(
508
508
  name,