robotcode-robot 0.94.0__py3-none-any.whl → 0.95.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|