5
5
import weakref
6
6
from collections import OrderedDict
7
7
from dataclasses import dataclass , field
8
+ from enum import Enum
8
9
from pathlib import Path
9
10
from typing import (
10
11
Any ,
38
39
from .imports_manager import ImportsManager
39
40
from .library_doc import (
40
41
BUILTIN_LIBRARY_NAME ,
42
+ BUILTIN_VARIABLES ,
41
43
DEFAULT_LIBRARIES ,
42
44
KeywordDoc ,
43
45
KeywordMatcher ,
@@ -61,15 +63,19 @@ class ImportError(DiagnosticsError):
61
63
62
64
63
65
@dataclass
64
- class Import :
65
- name : Optional [str ]
66
- name_token : Optional [Token ]
66
+ class SourceEntity :
67
67
line_no : int
68
68
col_offset : int
69
69
end_line_no : int
70
70
end_col_offset : int
71
71
source : str
72
72
73
+
74
+ @dataclass
75
+ class Import (SourceEntity ):
76
+ name : Optional [str ]
77
+ name_token : Optional [Token ]
78
+
73
79
def range (self ) -> Range :
74
80
return Range (
75
81
start = Position (
@@ -114,14 +120,55 @@ def __hash__(self) -> int:
114
120
class VariablesImport (Import ):
115
121
args : Tuple [str , ...] = ()
116
122
123
+ def __hash__ (self ) -> int :
124
+ return hash (
125
+ (
126
+ type (self ),
127
+ self .name ,
128
+ self .args ,
129
+ )
130
+ )
131
+
132
+
133
+ class VariableDefinitionType (Enum ):
134
+ VARIABLE = "Variable"
135
+ ARGUMENT = "Argument"
136
+ BUILTIN_VARIABLE = "Variable (Builtin)"
137
+
138
+
139
+ @dataclass
140
+ class VariableDefinition (SourceEntity ):
141
+ name : Optional [str ]
142
+ name_token : Optional [Token ]
143
+ type : VariableDefinitionType = VariableDefinitionType .VARIABLE
144
+
145
+ def __hash__ (self ) -> int :
146
+ return hash ((type (self ), self .name , self .type ))
147
+
148
+
149
+ @dataclass
150
+ class BuiltInVariableDefinition (VariableDefinition ):
151
+ type : VariableDefinitionType = VariableDefinitionType .BUILTIN_VARIABLE
152
+
153
+ def __hash__ (self ) -> int :
154
+ return hash ((type (self ), self .name , self .type ))
155
+
156
+
157
+ @dataclass
158
+ class ArgumentDefinition (VariableDefinition ):
159
+ type : VariableDefinitionType = VariableDefinitionType .ARGUMENT
160
+
161
+ def __hash__ (self ) -> int :
162
+ return hash ((type (self ), self .name , self .type ))
163
+
117
164
118
165
class NameSpaceError (Exception ):
119
166
pass
120
167
121
168
122
169
class VariablesVisitor (AsyncVisitor ):
123
- async def get (self , source : str , model : ast .AST ) -> List [str ]:
124
- self ._results : List [str ] = []
170
+ async def get (self , source : str , model : ast .AST ) -> List [VariableDefinition ]:
171
+ self ._results : List [VariableDefinition ] = []
125
172
self .source = source
126
173
await self .visit (model )
127
174
return self ._results
@@ -133,11 +180,58 @@ async def visit_Section(self, node: ast.AST) -> None: # noqa: N802
133
180
await self .generic_visit (node )
134
181
135
182
async def visit_Variable (self , node : ast .AST ) -> None : # noqa: N802
183
+ from robot .parsing .lexer .tokens import Token
136
184
from robot .parsing .model .statements import Variable
137
185
138
186
n = cast (Variable , node )
187
+ name = n .get_value (Token .VARIABLE )
139
188
if n .name :
140
- self ._results .append (n .name )
189
+ self ._results .append (
190
+ VariableDefinition (
191
+ name = n .name ,
192
+ name_token = name if name is not None else None ,
193
+ line_no = node .lineno ,
194
+ col_offset = node .col_offset ,
195
+ end_line_no = node .end_lineno if node .end_lineno is not None else - 1 ,
196
+ end_col_offset = node .end_col_offset if node .end_col_offset is not None else - 1 ,
197
+ source = self .source ,
198
+ )
199
+ )
200
+
201
+
202
+ class ArgumentsVisitor (AsyncVisitor ):
203
+ async def get (self , source : str , model : ast .AST ) -> List [VariableDefinition ]:
204
+ self ._results : List [VariableDefinition ] = []
205
+ self .source = source
206
+ await self .visit (model )
207
+ return self ._results
208
+
209
+ async def visit_Section (self , node : ast .AST ) -> None : # noqa: N802
210
+ from robot .parsing .model .blocks import VariableSection
211
+
212
+ if isinstance (node , VariableSection ):
213
+ await self .generic_visit (node )
214
+
215
+ async def visit_Arguments (self , node : ast .AST ) -> None : # noqa: N802
216
+ from robot .parsing .lexer .tokens import Token as RobotToken
217
+ from robot .parsing .model .statements import Arguments
218
+ from robot .variables .search import is_variable
219
+
220
+ n = cast (Arguments , node )
221
+ arguments = n .get_tokens (RobotToken .ARGUMENT )
222
+ for argument in (cast (RobotToken , e ) for e in arguments ):
223
+ if is_variable (argument .value ):
224
+ self ._results .append (
225
+ ArgumentDefinition (
226
+ name = argument .value ,
227
+ name_token = argument ,
228
+ line_no = argument .lineno ,
229
+ col_offset = argument .col_offset ,
230
+ end_line_no = argument .lineno if argument .lineno is not None else - 1 ,
231
+ end_col_offset = argument .end_col_offset if argument .end_col_offset is not None else - 1 ,
232
+ source = self .source ,
233
+ )
234
+ )
141
235
142
236
143
237
class ImportVisitor (AsyncVisitor ):
@@ -590,18 +684,14 @@ def __str__(self) -> str:
590
684
@dataclass
591
685
class ResourceEntry (LibraryEntry ):
592
686
imports : List [Import ] = field (default_factory = lambda : [])
593
- variables : List [str ] = field (default_factory = lambda : [])
687
+ variables : List [VariableDefinition ] = field (default_factory = lambda : [])
594
688
595
689
596
690
@dataclass
597
691
class VariablesEntry (LibraryEntry ):
598
692
pass
599
693
600
694
601
- IMPORTS_KEY = object ()
602
- REFERENCED_DOCUMENTS_KEY = object ()
603
-
604
-
605
695
class Namespace :
606
696
_logger = LoggingDescriptor ()
607
697
@@ -632,7 +722,8 @@ def __init__(
632
722
self ._analyze_lock = asyncio .Lock ()
633
723
self ._library_doc : Optional [LibraryDoc ] = None
634
724
self ._imports : Optional [List [Import ]] = None
635
- self ._own_variables : Optional [List [str ]] = None
725
+ self ._own_variables : Optional [List [VariableDefinition ]] = None
726
+ self ._variables_definitions : Optional [Dict [str , VariableDefinition ]] = None
636
727
self ._diagnostics : List [Diagnostic ] = []
637
728
638
729
self ._keywords : Optional [List [KeywordDoc ]] = None
@@ -732,12 +823,42 @@ async def get_imports(self) -> List[Import]:
732
823
733
824
return self ._imports
734
825
735
- async def get_own_variables (self ) -> List [str ]:
826
+ async def get_own_variables (self ) -> List [VariableDefinition ]:
736
827
if self ._own_variables is None :
737
828
self ._own_variables = await VariablesVisitor ().get (self .source , self .model )
738
829
739
830
return self ._own_variables
740
831
832
+ _builtin_variables : Optional [List [BuiltInVariableDefinition ]] = None
833
+
834
+ @classmethod
835
+ def get_builtin_variables (cls ) -> List [BuiltInVariableDefinition ]:
836
+ if cls ._builtin_variables is None :
837
+ cls ._builtin_variables = [BuiltInVariableDefinition (0 , 0 , 0 , 0 , "" , n , None ) for n in BUILTIN_VARIABLES ]
838
+
839
+ return cls ._builtin_variables
840
+
841
+ async def get_variables (self , nodes : Optional [List [ast .AST ]] = None ) -> Dict [str , VariableDefinition ]:
842
+ from robot .parsing .model .blocks import Keyword
843
+
844
+ await self ._ensure_initialized ()
845
+
846
+ if self ._variables_definitions is None :
847
+ result : Dict [str , VariableDefinition ] = {}
848
+
849
+ async for var in async_chain (
850
+ * [await ArgumentsVisitor ().get (self .source , n ) for n in nodes or [] if isinstance (n , Keyword )],
851
+ (e for e in await self .get_own_variables ()),
852
+ * (e .variables for e in self ._resources .values ()),
853
+ (e for e in self .get_builtin_variables ()),
854
+ ):
855
+ if var .name is not None and var .name not in result .keys ():
856
+ result [var .name ] = var
857
+
858
+ self ._variables_definitions = result
859
+
860
+ return self ._variables_definitions
861
+
741
862
async def _import_imports (self , imports : Iterable [Import ], base_dir : str , * , top_level : bool = False ) -> None :
742
863
async def _import (value : Import ) -> Optional [LibraryEntry ]:
743
864
result : Optional [LibraryEntry ] = None
0 commit comments