robotcode-robot 0.94.0__py3-none-any.whl → 0.95.1__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/robot/__version__.py +1 -1
- robotcode/robot/diagnostics/data_cache.py +83 -0
- robotcode/robot/diagnostics/entities.py +3 -3
- robotcode/robot/diagnostics/errors.py +1 -1
- robotcode/robot/diagnostics/imports_manager.py +100 -99
- robotcode/robot/diagnostics/keyword_finder.py +69 -43
- robotcode/robot/diagnostics/library_doc.py +197 -151
- robotcode/robot/diagnostics/model_helper.py +10 -6
- robotcode/robot/diagnostics/namespace.py +17 -28
- robotcode/robot/diagnostics/namespace_analyzer.py +120 -43
- robotcode/robot/utils/ast.py +0 -7
- robotcode/robot/utils/match.py +2 -2
- robotcode/robot/utils/robot_path.py +15 -16
- robotcode/robot/utils/stubs.py +1 -19
- robotcode/robot/utils/variables.py +35 -0
- robotcode/robot/utils/visitor.py +0 -27
- {robotcode_robot-0.94.0.dist-info → robotcode_robot-0.95.1.dist-info}/METADATA +2 -2
- robotcode_robot-0.95.1.dist-info/RECORD +32 -0
- robotcode_robot-0.94.0.dist-info/RECORD +0 -31
- {robotcode_robot-0.94.0.dist-info → robotcode_robot-0.95.1.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.94.0.dist-info → robotcode_robot-0.95.1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
import functools
|
2
|
+
import re
|
1
3
|
from itertools import chain
|
2
4
|
from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, NamedTuple, Optional, Sequence, Tuple
|
3
5
|
|
@@ -42,6 +44,8 @@ class KeywordFinder:
|
|
42
44
|
self.self_library_doc = library_doc
|
43
45
|
|
44
46
|
self.diagnostics: List[DiagnosticsEntry] = []
|
47
|
+
self.result_bdd_prefix: Optional[str] = None
|
48
|
+
|
45
49
|
self.multiple_keywords_result: Optional[List[KeywordDoc]] = None
|
46
50
|
self._cache: Dict[
|
47
51
|
Tuple[Optional[str], bool],
|
@@ -49,17 +53,20 @@ class KeywordFinder:
|
|
49
53
|
Optional[KeywordDoc],
|
50
54
|
List[DiagnosticsEntry],
|
51
55
|
Optional[List[KeywordDoc]],
|
56
|
+
Optional[str],
|
52
57
|
],
|
53
58
|
] = {}
|
54
|
-
|
59
|
+
|
55
60
|
self._all_keywords: Optional[List[LibraryEntry]] = None
|
56
|
-
self.
|
57
|
-
self.
|
61
|
+
self._resource_imports: Optional[List[ResourceEntry]] = None
|
62
|
+
self._library_imports: Optional[List[LibraryEntry]] = None
|
58
63
|
|
59
64
|
def reset_diagnostics(self) -> None:
|
60
65
|
self.diagnostics = []
|
61
66
|
self.multiple_keywords_result = None
|
67
|
+
self.result_bdd_prefix = None
|
62
68
|
|
69
|
+
# TODO: make this threadsafe
|
63
70
|
def find_keyword(
|
64
71
|
self,
|
65
72
|
name: Optional[str],
|
@@ -70,17 +77,16 @@ class KeywordFinder:
|
|
70
77
|
try:
|
71
78
|
self.reset_diagnostics()
|
72
79
|
|
73
|
-
self.handle_bdd_style
|
74
|
-
|
75
|
-
cached = self._cache.get((name, self.handle_bdd_style), None)
|
80
|
+
cached = self._cache.get((name, handle_bdd_style), None)
|
76
81
|
|
77
82
|
if cached is not None:
|
78
83
|
self.diagnostics = cached[1]
|
79
84
|
self.multiple_keywords_result = cached[2]
|
85
|
+
self.result_bdd_prefix = cached[3]
|
80
86
|
return cached[0]
|
81
87
|
|
82
88
|
try:
|
83
|
-
result = self._find_keyword(name)
|
89
|
+
result = self._find_keyword(name, handle_bdd_style)
|
84
90
|
if result is None:
|
85
91
|
self.diagnostics.append(
|
86
92
|
DiagnosticsEntry(
|
@@ -99,17 +105,22 @@ class KeywordFinder:
|
|
99
105
|
result = None
|
100
106
|
self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR))
|
101
107
|
|
102
|
-
self._cache[(name,
|
108
|
+
self._cache[(name, handle_bdd_style)] = (
|
103
109
|
result,
|
104
110
|
self.diagnostics,
|
105
111
|
self.multiple_keywords_result,
|
112
|
+
self.result_bdd_prefix,
|
106
113
|
)
|
107
114
|
|
108
115
|
return result
|
109
116
|
except CancelSearchError:
|
110
117
|
return None
|
111
118
|
|
112
|
-
def _find_keyword(
|
119
|
+
def _find_keyword(
|
120
|
+
self,
|
121
|
+
name: Optional[str],
|
122
|
+
handle_bdd_style: bool = True,
|
123
|
+
) -> Optional[KeywordDoc]:
|
113
124
|
if not name:
|
114
125
|
self.diagnostics.append(
|
115
126
|
DiagnosticsEntry(
|
@@ -129,14 +140,21 @@ class KeywordFinder:
|
|
129
140
|
)
|
130
141
|
raise CancelSearchError
|
131
142
|
|
132
|
-
result =
|
143
|
+
result: Optional[KeywordDoc] = None
|
144
|
+
|
145
|
+
if get_robot_version() >= (7, 0) and handle_bdd_style:
|
146
|
+
result = self._get_bdd_style_keyword(name)
|
147
|
+
|
148
|
+
if not result:
|
149
|
+
result = self._get_keyword_from_self(name)
|
150
|
+
|
133
151
|
if not result and "." in name:
|
134
152
|
result = self._get_explicit_keyword(name)
|
135
153
|
|
136
154
|
if not result:
|
137
155
|
result = self._get_implicit_keyword(name)
|
138
156
|
|
139
|
-
if not result and
|
157
|
+
if get_robot_version() < (7, 0) and not result and handle_bdd_style:
|
140
158
|
return self._get_bdd_style_keyword(name)
|
141
159
|
|
142
160
|
return result
|
@@ -264,6 +282,9 @@ class KeywordFinder:
|
|
264
282
|
def _select_best_matches(
|
265
283
|
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
266
284
|
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
285
|
+
if len(entries) < 2:
|
286
|
+
return entries
|
287
|
+
|
267
288
|
normal = [hand for hand in entries if not hand[1].is_embedded]
|
268
289
|
if normal:
|
269
290
|
return normal
|
@@ -291,23 +312,23 @@ class KeywordFinder:
|
|
291
312
|
other: Tuple[Optional[LibraryEntry], KeywordDoc],
|
292
313
|
) -> bool:
|
293
314
|
return (
|
294
|
-
other[1].matcher.embedded_arguments
|
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
|
295
318
|
and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
|
296
319
|
)
|
297
320
|
|
298
321
|
def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
|
299
|
-
if self.
|
300
|
-
self.
|
322
|
+
if self._resource_imports is None:
|
323
|
+
self._resource_imports = list(chain(self.namespace._resources.values()))
|
301
324
|
|
302
325
|
if get_robot_version() >= (6, 0):
|
303
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
304
|
-
|
305
|
-
|
306
|
-
if r:
|
307
|
-
found.extend([(v, k) for k in r])
|
326
|
+
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
327
|
+
(v, k) for v in self._resource_imports for k in v.library_doc.keywords.iter_all(name)
|
328
|
+
]
|
308
329
|
else:
|
309
330
|
found = []
|
310
|
-
for k in self.
|
331
|
+
for k in self._resource_imports:
|
311
332
|
s = k.library_doc.keywords.get(name, None)
|
312
333
|
if s is not None:
|
313
334
|
found.append((k, s))
|
@@ -352,19 +373,18 @@ class KeywordFinder:
|
|
352
373
|
return entries
|
353
374
|
|
354
375
|
def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
|
355
|
-
if self.
|
356
|
-
self.
|
376
|
+
if self._library_imports is None:
|
377
|
+
self._library_imports = list(chain(self.namespace._libraries.values()))
|
357
378
|
|
358
379
|
if get_robot_version() >= (6, 0):
|
359
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
found.extend([(v, k) for k in r])
|
380
|
+
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
381
|
+
(v, k) for v in self._library_imports for k in v.library_doc.keywords.iter_all(name)
|
382
|
+
]
|
383
|
+
|
364
384
|
else:
|
365
385
|
found = []
|
366
386
|
|
367
|
-
for k in self.
|
387
|
+
for k in self._library_imports:
|
368
388
|
s = k.library_doc.keywords.get(name, None)
|
369
389
|
if s is not None:
|
370
390
|
found.append((k, s))
|
@@ -438,21 +458,27 @@ class KeywordFinder:
|
|
438
458
|
f"or '{'' if standard[0] is None else standard[0].alias or standard[0].name}.{standard[1].name}'."
|
439
459
|
)
|
440
460
|
|
461
|
+
@functools.cached_property
|
462
|
+
def bdd_prefix_regexp(self) -> "re.Pattern[str]":
|
463
|
+
prefixes = (
|
464
|
+
"|".join(
|
465
|
+
self.namespace.languages.bdd_prefixes
|
466
|
+
if self.namespace.languages is not None
|
467
|
+
else ["given", "when", "then", "and", "but"]
|
468
|
+
)
|
469
|
+
.replace(" ", r"\s")
|
470
|
+
.lower()
|
471
|
+
)
|
472
|
+
return re.compile(rf"({prefixes})\s", re.IGNORECASE)
|
473
|
+
|
441
474
|
def _get_bdd_style_keyword(self, name: str) -> Optional[KeywordDoc]:
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
if
|
446
|
-
|
447
|
-
|
475
|
+
match = self.bdd_prefix_regexp.match(name)
|
476
|
+
if match:
|
477
|
+
result = self._find_keyword(
|
478
|
+
name[match.end() :], handle_bdd_style=False if get_robot_version() >= (7, 0) else True
|
479
|
+
)
|
480
|
+
if result:
|
481
|
+
self.result_bdd_prefix = str(match.group(0))
|
448
482
|
|
449
|
-
|
450
|
-
if len(parts) < 2:
|
451
|
-
return None
|
452
|
-
for index in range(1, len(parts)):
|
453
|
-
prefix = " ".join(parts[:index]).title()
|
454
|
-
if prefix.title() in (
|
455
|
-
self.namespace.languages.bdd_prefixes if self.namespace.languages is not None else DEFAULT_BDD_PREFIXES
|
456
|
-
):
|
457
|
-
return self._find_keyword(" ".join(parts[index:]))
|
483
|
+
return result
|
458
484
|
return None
|