2
2
3
3
import ast
4
4
import asyncio
5
+ import itertools
5
6
import weakref
6
7
from collections import OrderedDict
7
8
from dataclasses import dataclass , field
20
21
Tuple ,
21
22
cast ,
22
23
)
23
- import itertools
24
24
25
25
from ....utils .async_itertools import async_chain
26
26
from ....utils .logging import LoggingDescriptor
35
35
Position ,
36
36
Range ,
37
37
)
38
- from ..utils .ast import Token , is_non_variable_token , range_from_token_or_node
38
+ from ..utils .ast import (
39
+ Token ,
40
+ is_non_variable_token ,
41
+ range_from_token_or_node ,
42
+ tokenize_variables ,
43
+ )
39
44
from ..utils .async_ast import AsyncVisitor
40
45
from .imports_manager import ImportsManager
41
46
from .library_doc import (
@@ -220,6 +225,46 @@ async def get(self, source: str, model: ast.AST) -> List[VariableDefinition]:
220
225
await self .visit (model )
221
226
return self ._results
222
227
228
+ async def visit_KeywordName (self , node : ast .AST ) -> None : # noqa: N802
229
+ from robot .parsing .lexer .tokens import Token as RobotToken
230
+ from robot .parsing .model .statements import KeywordName
231
+ from robot .variables .search import VariableSearcher
232
+
233
+ n = cast (KeywordName , node )
234
+ name_token = cast (Token , n .get_token (RobotToken .KEYWORD_NAME ))
235
+
236
+ if name_token is not None and name_token .value :
237
+ for a in filter (
238
+ lambda e : e .type == RobotToken .VARIABLE ,
239
+ tokenize_variables (name_token , identifiers = "$" , ignore_errors = True ),
240
+ ):
241
+ if a .value :
242
+ searcher = VariableSearcher ("$" , ignore_errors = True )
243
+ match = searcher .search (a .value )
244
+ if match .base is None :
245
+ continue
246
+ name = f"{ match .identifier } {{{ match .base .split (':' , 1 )[0 ]} }}"
247
+
248
+ self ._results .append (
249
+ ArgumentDefinition (
250
+ name = name ,
251
+ name_token = a ,
252
+ line_no = a .lineno ,
253
+ col_offset = node .col_offset ,
254
+ end_line_no = node .end_lineno
255
+ if node .end_lineno is not None
256
+ else a .lineno
257
+ if a .lineno is not None
258
+ else - 1 ,
259
+ end_col_offset = node .end_col_offset
260
+ if node .end_col_offset is not None
261
+ else a .end_col_offset
262
+ if name_token .end_col_offset is not None
263
+ else - 1 ,
264
+ source = self .source ,
265
+ )
266
+ )
267
+
223
268
async def visit_Arguments (self , node : ast .AST ) -> None : # noqa: N802
224
269
from robot .errors import VariableError
225
270
from robot .parsing .lexer .tokens import Token as RobotToken
@@ -781,7 +826,7 @@ def __init__(
781
826
self ._libraries : OrderedDict [str , LibraryEntry ] = OrderedDict ()
782
827
self ._resources : OrderedDict [str , ResourceEntry ] = OrderedDict ()
783
828
self ._variables : OrderedDict [str , VariablesEntry ] = OrderedDict ()
784
- self ._initialzed = False
829
+ self ._initialized = False
785
830
self ._initialize_lock = asyncio .Lock ()
786
831
self ._analyzed = False
787
832
self ._analyze_lock = asyncio .Lock ()
@@ -791,6 +836,7 @@ def __init__(
791
836
self ._diagnostics : List [Diagnostic ] = []
792
837
793
838
self ._keywords : Optional [List [KeywordDoc ]] = None
839
+ self ._loop = asyncio .get_event_loop ()
794
840
795
841
# TODO: how to get the search order from model
796
842
self .search_order : Tuple [str , ...] = ()
@@ -814,21 +860,21 @@ async def resources_changed(self, sender: Any, params: List[LibraryDoc]) -> None
814
860
815
861
@_logger .call
816
862
async def get_diagnostisc (self ) -> List [Diagnostic ]:
817
- await self ._ensure_initialized ()
863
+ await self .ensure_initialized ()
818
864
819
865
await self ._analyze ()
820
866
821
867
return self ._diagnostics
822
868
823
869
@_logger .call
824
870
async def get_libraries (self ) -> OrderedDict [str , LibraryEntry ]:
825
- await self ._ensure_initialized ()
871
+ await self .ensure_initialized ()
826
872
827
873
return self ._libraries
828
874
829
875
@_logger .call
830
876
async def get_resources (self ) -> OrderedDict [str , ResourceEntry ]:
831
- await self ._ensure_initialized ()
877
+ await self .ensure_initialized ()
832
878
833
879
return self ._resources
834
880
@@ -856,9 +902,9 @@ async def get_library_doc(self) -> LibraryDoc:
856
902
return self ._library_doc
857
903
858
904
@_logger .call
859
- async def _ensure_initialized (self ) -> None :
905
+ async def ensure_initialized (self ) -> bool :
860
906
async with self ._initialize_lock :
861
- if not self ._initialzed :
907
+ if not self ._initialized :
862
908
imports = await self .get_imports ()
863
909
864
910
if self .document is not None :
@@ -879,7 +925,12 @@ async def _ensure_initialized(self) -> None:
879
925
await self ._import_default_libraries ()
880
926
await self ._import_imports (imports , str (Path (self .source ).parent ), top_level = True )
881
927
882
- self ._initialzed = True
928
+ self ._initialized = True
929
+ return self ._initialized
930
+
931
+ @property
932
+ def initialized (self ) -> bool :
933
+ return self ._initialized
883
934
884
935
async def get_imports (self ) -> List [Import ]:
885
936
if self ._imports is None :
@@ -905,7 +956,7 @@ def get_builtin_variables(cls) -> List[BuiltInVariableDefinition]:
905
956
async def get_variables (self , nodes : Optional [List [ast .AST ]] = None ) -> Dict [VariableMatcher , VariableDefinition ]:
906
957
from robot .parsing .model .blocks import Keyword , TestCase
907
958
908
- await self ._ensure_initialized ()
959
+ await self .ensure_initialized ()
909
960
910
961
result : Dict [VariableMatcher , VariableDefinition ] = {}
911
962
@@ -1246,7 +1297,7 @@ async def _get_resource_entry(self, name: str, base_dir: str, sentinel: Any = No
1246
1297
1247
1298
@_logger .call
1248
1299
async def get_keywords (self ) -> List [KeywordDoc ]:
1249
- await self ._ensure_initialized ()
1300
+ await self .ensure_initialized ()
1250
1301
1251
1302
if self ._keywords is None :
1252
1303
result : Dict [KeywordMatcher , KeywordDoc ] = {}
@@ -1296,10 +1347,13 @@ async def _analyze(self) -> None:
1296
1347
self ._analyzed = True
1297
1348
1298
1349
async def find_keyword (self , name : Optional [str ]) -> Optional [KeywordDoc ]:
1299
- await self ._ensure_initialized ()
1350
+ await self .ensure_initialized ()
1300
1351
1301
1352
return await KeywordFinder (self ).find_keyword (name )
1302
1353
1354
+ async def find_keyword_threadsafe (self , name : Optional [str ]) -> Optional [KeywordDoc ]:
1355
+ return await asyncio .wrap_future (asyncio .run_coroutine_threadsafe (self .find_keyword (name ), self ._loop ))
1356
+
1303
1357
1304
1358
class DiagnosticsEntry (NamedTuple ):
1305
1359
message : str
0 commit comments