Skip to content

Commit 2c55ad8

Browse files
committed
implement support for resolving variables values at design time
1 parent 9706e32 commit 2c55ad8

File tree

6 files changed

+230
-70
lines changed

6 files changed

+230
-70
lines changed

robotcode/language_server/robotframework/diagnostics/entities.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22
from enum import Enum
3-
from typing import Optional, Tuple
3+
from typing import Any, Optional, Tuple
44

55
from ...common.lsp_types import Position, Range
66
from ..utils.ast import Token
@@ -81,14 +81,20 @@ class VariableDefinitionType(Enum):
8181
COMMAND_LINE_VARIABLE = "command line variable"
8282
BUILTIN_VARIABLE = "builtin variable"
8383
IMPORTED_VARIABLE = "imported variable"
84+
ENVIRONMENT_VARIABLE = "environment variable"
8485

8586

8687
@dataclass
8788
class VariableDefinition(SourceEntity):
88-
name: Optional[str]
89+
name: str
8990
name_token: Optional[Token]
9091
type: VariableDefinitionType = VariableDefinitionType.VARIABLE
9192

93+
has_value: bool = False
94+
resolvable: bool = False
95+
96+
value: Any = None
97+
9298
def __hash__(self) -> int:
9399
return hash((type(self), self.name, self.type, self.range, self.source))
94100

@@ -116,6 +122,7 @@ def __hash__(self) -> int:
116122
@dataclass
117123
class BuiltInVariableDefinition(VariableDefinition):
118124
type: VariableDefinitionType = VariableDefinitionType.BUILTIN_VARIABLE
125+
resolvable: bool = True
119126

120127
def __hash__(self) -> int:
121128
return hash((type(self), self.name, self.type))
@@ -124,6 +131,7 @@ def __hash__(self) -> int:
124131
@dataclass
125132
class CommandLineVariableDefinition(VariableDefinition):
126133
type: VariableDefinitionType = VariableDefinitionType.COMMAND_LINE_VARIABLE
134+
resolvable: bool = True
127135

128136
def __hash__(self) -> int:
129137
return hash((type(self), self.name, self.type))
@@ -143,3 +151,12 @@ class ImportedVariableDefinition(VariableDefinition):
143151

144152
def __hash__(self) -> int:
145153
return hash((type(self), self.name, self.type))
154+
155+
156+
@dataclass
157+
class EnvironmentVariableDefinition(VariableDefinition):
158+
type: VariableDefinitionType = VariableDefinitionType.ENVIRONMENT_VARIABLE
159+
resolvable: bool = True
160+
161+
def __hash__(self) -> int:
162+
return hash((type(self), self.name, self.type))

robotcode/language_server/robotframework/diagnostics/imports_manager.py

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Any,
1313
Callable,
1414
Coroutine,
15+
Dict,
1516
List,
1617
Optional,
1718
Tuple,
@@ -52,6 +53,7 @@
5253
get_library_doc,
5354
get_variables_doc,
5455
is_embedded_keyword,
56+
resolve_variable,
5557
)
5658

5759
RESOURCE_EXTENSIONS = (".resource", ".robot", ".txt", ".tsv", ".rst", ".rest")
@@ -475,8 +477,8 @@ def get_command_line_variables(self) -> List[VariableDefinition]:
475477
self._command_line_variables = []
476478
else:
477479
self._command_line_variables = [
478-
CommandLineVariableDefinition(0, 0, 0, 0, "", f"${{{k}}}", None)
479-
for k in self.config.variables.keys()
480+
CommandLineVariableDefinition(0, 0, 0, 0, "", f"${{{k}}}", None, has_value=True, value=(v,))
481+
for k, v in self.config.variables.items()
480482
]
481483

482484
return self._command_line_variables
@@ -616,7 +618,7 @@ async def remove(k: _VariablesEntryKey, e: _VariablesEntry) -> None:
616618
pass
617619

618620
@_logger.call
619-
async def find_library(self, name: str, base_dir: str) -> str:
621+
async def find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
620622
return await asyncio.wait_for(
621623
asyncio.get_running_loop().run_in_executor(
622624
self.process_pool,
@@ -626,17 +628,26 @@ async def find_library(self, name: str, base_dir: str) -> str:
626628
base_dir,
627629
self.config.python_path if self.config is not None else None,
628630
self.config.env if self.config is not None else None,
629-
self.config.variables if self.config is not None else None,
631+
variables if variables is not None else self.config.variables if self.config is not None else None,
630632
),
631633
FIND_FILE_TIME_OUT,
632634
)
633635

634636
@_logger.call
635637
async def get_libdoc_for_library_import(
636-
self, name: str, args: Tuple[Any, ...], base_dir: str, sentinel: Any = None
638+
self,
639+
name: str,
640+
args: Tuple[Any, ...],
641+
base_dir: str,
642+
sentinel: Any = None,
643+
variables: Optional[Dict[str, Any]] = None,
637644
) -> LibraryDoc:
638645

639-
source = await self.find_library(name, base_dir)
646+
source = await self.find_library(
647+
name,
648+
base_dir,
649+
variables,
650+
)
640651

641652
async def _get_libdoc() -> LibraryDoc:
642653
self._logger.debug(lambda: f"Load Library {source}{repr(args)}")
@@ -651,7 +662,7 @@ async def _get_libdoc() -> LibraryDoc:
651662
base_dir,
652663
self.config.python_path if self.config is not None else None,
653664
self.config.env if self.config is not None else None,
654-
self.config.variables if self.config is not None else None,
665+
variables if variables is not None else self.config.variables if self.config is not None else None,
655666
),
656667
LOAD_LIBRARY_TIME_OUT,
657668
)
@@ -785,9 +796,19 @@ def _create_handler(self, kw: Any) -> Any:
785796

786797
@_logger.call
787798
async def get_libdoc_for_variables_import(
788-
self, name: str, args: Tuple[Any, ...], base_dir: str, sentinel: Any = None
799+
self,
800+
name: str,
801+
args: Tuple[Any, ...],
802+
base_dir: str,
803+
sentinel: Any = None,
804+
variables: Optional[Dict[str, Any]] = None,
789805
) -> VariablesDoc:
790-
source = await self.find_file(name, base_dir, "Variables")
806+
source = await self.find_file(
807+
name,
808+
base_dir,
809+
"Variables",
810+
variables,
811+
)
791812

792813
async def _get_libdoc() -> VariablesDoc:
793814
self._logger.debug(lambda: f"Load variables {source}{repr(args)}")
@@ -829,7 +850,9 @@ async def _get_libdoc() -> VariablesDoc:
829850
return await entry.get_libdoc()
830851

831852
@_logger.call
832-
async def find_file(self, name: str, base_dir: str, file_type: str = "Resource") -> str:
853+
async def find_file(
854+
self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None
855+
) -> str:
833856
result = await asyncio.wait_for(
834857
asyncio.get_running_loop().run_in_executor(
835858
self.process_pool,
@@ -839,7 +862,7 @@ async def find_file(self, name: str, base_dir: str, file_type: str = "Resource")
839862
base_dir,
840863
self.config.python_path if self.config is not None else None,
841864
self.config.env if self.config is not None else None,
842-
self.config.variables if self.config is not None else None,
865+
variables if variables is not None else self.config.variables if self.config is not None else None,
843866
file_type,
844867
),
845868
FIND_FILE_TIME_OUT,
@@ -848,8 +871,10 @@ async def find_file(self, name: str, base_dir: str, file_type: str = "Resource")
848871
return result
849872

850873
@_logger.call
851-
async def _get_entry_for_resource_import(self, name: str, base_dir: str, sentinel: Any = None) -> _ResourcesEntry:
852-
source = await self.find_file(name, base_dir)
874+
async def _get_entry_for_resource_import(
875+
self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None
876+
) -> _ResourcesEntry:
877+
source = await self.find_file(name, base_dir, variables=variables)
853878

854879
async def _get_document() -> TextDocument:
855880
self._logger.debug(lambda: f"Load resource {name} from source {source}")
@@ -880,23 +905,38 @@ async def _get_document() -> TextDocument:
880905

881906
return entry
882907

883-
@_logger.call
884-
async def get_document_for_resource_import(self, name: str, base_dir: str, sentinel: Any = None) -> TextDocument:
885-
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel)
908+
async def get_namespace_and_libdoc_for_resource_import(
909+
self,
910+
name: str,
911+
base_dir: str,
912+
sentinel: Any = None,
913+
variables: Optional[Dict[str, Any]] = None,
914+
) -> Tuple["Namespace", LibraryDoc]:
915+
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
886916

887-
return await entry.get_document()
917+
return await entry.get_namespace(), await entry.get_libdoc()
888918

889-
async def get_namespace_for_resource_import(self, name: str, base_dir: str, sentinel: Any = None) -> "Namespace":
890-
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel)
919+
async def get_namespace_for_resource_import(
920+
self,
921+
name: str,
922+
base_dir: str,
923+
sentinel: Any = None,
924+
variables: Optional[Dict[str, Any]] = None,
925+
) -> "Namespace":
926+
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
891927

892928
return await entry.get_namespace()
893929

894-
async def get_libdoc_for_resource_import(self, name: str, base_dir: str, sentinel: Any = None) -> LibraryDoc:
895-
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel)
930+
async def get_libdoc_for_resource_import(
931+
self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None
932+
) -> LibraryDoc:
933+
entry = await self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
896934

897935
return await entry.get_libdoc()
898936

899-
async def complete_library_import(self, name: Optional[str], base_dir: str = ".") -> Optional[List[CompleteResult]]:
937+
async def complete_library_import(
938+
self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None
939+
) -> Optional[List[CompleteResult]]:
900940
result = await asyncio.wait_for(
901941
asyncio.get_running_loop().run_in_executor(
902942
self.process_pool,
@@ -906,15 +946,15 @@ async def complete_library_import(self, name: Optional[str], base_dir: str = "."
906946
base_dir,
907947
self.config.python_path if self.config is not None else None,
908948
self.config.env if self.config is not None else None,
909-
self.config.variables if self.config is not None else None,
949+
variables if variables is not None else self.config.variables if self.config is not None else None,
910950
),
911951
COMPLETE_LIBRARY_IMPORT_TIME_OUT,
912952
)
913953

914954
return result
915955

916956
async def complete_resource_import(
917-
self, name: Optional[str], base_dir: str = "."
957+
self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None
918958
) -> Optional[List[CompleteResult]]:
919959
result = await asyncio.wait_for(
920960
asyncio.get_running_loop().run_in_executor(
@@ -925,15 +965,15 @@ async def complete_resource_import(
925965
base_dir,
926966
self.config.python_path if self.config is not None else None,
927967
self.config.env if self.config is not None else None,
928-
self.config.variables if self.config is not None else None,
968+
variables if variables is not None else self.config.variables if self.config is not None else None,
929969
),
930970
COMPLETE_RESOURCE_IMPORT_TIME_OUT,
931971
)
932972

933973
return result
934974

935975
async def complete_variables_import(
936-
self, name: Optional[str], base_dir: str = "."
976+
self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None
937977
) -> Optional[List[CompleteResult]]:
938978
result = await asyncio.wait_for(
939979
asyncio.get_running_loop().run_in_executor(
@@ -944,7 +984,25 @@ async def complete_variables_import(
944984
base_dir,
945985
self.config.python_path if self.config is not None else None,
946986
self.config.env if self.config is not None else None,
947-
self.config.variables if self.config is not None else None,
987+
variables if variables is not None else self.config.variables if self.config is not None else None,
988+
),
989+
COMPLETE_VARIABLES_IMPORT_TIME_OUT,
990+
)
991+
992+
return result
993+
994+
async def resolve_variable(
995+
self, name: str, base_dir: str = ".", variables: Optional[Dict[str, Any]] = None, ignore_errors: bool = True
996+
) -> Any:
997+
result = await asyncio.wait_for(
998+
asyncio.get_running_loop().run_in_executor(
999+
self.process_pool,
1000+
resolve_variable,
1001+
name,
1002+
str(self.folder.to_path()),
1003+
base_dir,
1004+
variables if variables is not None else self.config.variables if self.config is not None else None,
1005+
ignore_errors,
9481006
),
9491007
COMPLETE_VARIABLES_IMPORT_TIME_OUT,
9501008
)

0 commit comments

Comments
 (0)