6464 CompleteResult ,
6565 LibraryDoc ,
6666 ModuleSpec ,
67+ ResourceDoc ,
6768 VariablesDoc ,
6869 complete_library_import ,
6970 complete_resource_import ,
@@ -307,7 +308,7 @@ def __init__(
307308 self .name = name
308309 self .source_path = source_path
309310 self ._document : Optional [TextDocument ] = None
310- self ._lib_doc : Optional [LibraryDoc ] = None
311+ self ._lib_doc : Optional [ResourceDoc ] = None
311312
312313 def __repr__ (self ) -> str :
313314 return f"{ type (self ).__qualname__ } (name={ self .name !r} , file_watchers={ self .file_watchers !r} , id={ id (self )!r} "
@@ -392,13 +393,16 @@ def get_namespace(self) -> "Namespace":
392393 def _get_namespace (self ) -> "Namespace" :
393394 return self .parent .get_namespace_for_resource (self ._get_document ())
394395
395- def get_libdoc (self ) -> LibraryDoc :
396+ def get_resource_doc (self ) -> ResourceDoc :
396397 with self ._lock :
397398 if self ._lib_doc is None :
398- self ._lib_doc = self ._get_namespace (). get_library_doc ( )
399+ self ._lib_doc = self .parent . get_resource_doc_from_document ( self . _get_document () )
399400
400401 return self ._lib_doc
401402
403+ def get_libdoc (self ) -> ResourceDoc :
404+ return self .get_resource_doc ()
405+
402406
403407@dataclass (frozen = True , slots = True )
404408class _VariablesEntryKey :
@@ -525,6 +529,18 @@ def filepath_base(self) -> str:
525529 raise ValueError ("Cannot determine filepath base." )
526530
527531
532+ @dataclass
533+ class RobotFileMeta :
534+ meta_version : str
535+ source : str
536+ mtime_ns : int
537+
538+ @property
539+ def filepath_base (self ) -> str :
540+ p = Path (self .source )
541+ return f"{ zlib .adler32 (str (p .parent ).encode ('utf-8' )):08x} _{ p .stem } { p .suffix } "
542+
543+
528544class ImportsManager :
529545 _logger = LoggingDescriptor ()
530546
@@ -613,7 +629,7 @@ def __init__(
613629 self ._resource_document_changed_timer_interval = 1
614630 self ._resource_document_changed_documents : Set [TextDocument ] = set ()
615631
616- self ._resource_libdoc_cache : "weakref.WeakKeyDictionary[ast.AST, Dict[str, LibraryDoc ]]" = (
632+ self ._resource_libdoc_cache : "weakref.WeakKeyDictionary[ast.AST, Dict[str, ResourceDoc ]]" = (
617633 weakref .WeakKeyDictionary ()
618634 )
619635
@@ -676,6 +692,17 @@ def environment(self) -> Mapping[str, str]:
676692 def get_namespace_for_resource (self , document : TextDocument ) -> "Namespace" :
677693 return self .document_cache_helper .get_resource_namespace (document )
678694
695+ def get_resource_doc_from_document (self , document : TextDocument ) -> ResourceDoc :
696+ source = str (document .uri .to_path ())
697+
698+ if not self ._is_document_loaded (source ):
699+ cached = self ._get_model_doc_cached (source )
700+ if cached is not None :
701+ return cached
702+
703+ model = self .document_cache_helper .get_resource_model (document )
704+ return self .get_libdoc_from_model (model , source )
705+
679706 def clear_cache (self ) -> None :
680707 if self .cache_path .exists ():
681708 shutil .rmtree (self .cache_path , ignore_errors = True )
@@ -1407,17 +1434,29 @@ def get_libdoc_for_library_import(
14071434
14081435 return entry .get_libdoc ()
14091436
1437+ def _is_document_loaded (self , source : str ) -> bool :
1438+ doc = self .documents_manager .get (Uri .from_path (source ))
1439+ return doc is not None and doc .version is not None
1440+
14101441 @_logger .call
14111442 def get_libdoc_from_model (
14121443 self ,
14131444 model : ast .AST ,
14141445 source : str ,
1415- ) -> LibraryDoc :
1446+ ) -> ResourceDoc :
1447+
14161448 entry = self ._resource_libdoc_cache .get (model )
14171449 if entry is not None and source in entry :
14181450 return entry [source ]
14191451
1420- result = get_model_doc (model = model , source = source )
1452+ use_disk_cache = not self ._is_document_loaded (source )
1453+
1454+ result = self ._get_model_doc_cached (source ) if use_disk_cache else None
1455+ if result is None :
1456+ result = get_model_doc (model = model , source = source )
1457+ if use_disk_cache :
1458+ self ._save_model_doc_cache (source , result )
1459+
14211460 if entry is None :
14221461 entry = {}
14231462 self ._resource_libdoc_cache [model ] = entry
@@ -1426,6 +1465,62 @@ def get_libdoc_from_model(
14261465
14271466 return result
14281467
1468+ @staticmethod
1469+ def get_resource_meta (source : str ) -> Optional [RobotFileMeta ]:
1470+ try :
1471+ source_path = normalized_path (source )
1472+ if source_path .exists ():
1473+ return RobotFileMeta (
1474+ __version__ ,
1475+ str (source_path ),
1476+ os .stat (source_path , follow_symlinks = False ).st_mtime_ns ,
1477+ )
1478+ except OSError :
1479+ pass
1480+ return None
1481+
1482+ def _get_model_doc_cached (self , source : str ) -> Optional [ResourceDoc ]:
1483+ meta = self .get_resource_meta (source )
1484+ if meta is None :
1485+ return None
1486+
1487+ meta_file = meta .filepath_base + ".meta"
1488+ if not self .data_cache .cache_data_exists (CacheSection .RESOURCE , meta_file ):
1489+ return None
1490+
1491+ try :
1492+ saved_meta = self .data_cache .read_cache_data (CacheSection .RESOURCE , meta_file , RobotFileMeta )
1493+ if saved_meta == meta :
1494+ spec_file = meta .filepath_base + ".spec"
1495+ return self .data_cache .read_cache_data (CacheSection .RESOURCE , spec_file , ResourceDoc )
1496+ except (SystemExit , KeyboardInterrupt ):
1497+ raise
1498+ except BaseException as e :
1499+ ex = e
1500+ self ._logger .debug (
1501+ lambda : f"Failed to load cached model doc for { source } : { ex } " ,
1502+ context_name = "import" ,
1503+ )
1504+
1505+ return None
1506+
1507+ def _save_model_doc_cache (self , source : str , result : ResourceDoc ) -> None :
1508+ meta = self .get_resource_meta (source )
1509+ if meta is None :
1510+ return
1511+
1512+ try :
1513+ self .data_cache .save_cache_data (CacheSection .RESOURCE , meta .filepath_base + ".spec" , result )
1514+ self .data_cache .save_cache_data (CacheSection .RESOURCE , meta .filepath_base + ".meta" , meta )
1515+ except (SystemExit , KeyboardInterrupt ):
1516+ raise
1517+ except BaseException as e :
1518+ ex = e
1519+ self ._logger .debug (
1520+ lambda : f"Failed to save model doc cache for { source } : { ex } " ,
1521+ context_name = "import" ,
1522+ )
1523+
14291524 def _get_variables_libdoc (
14301525 self ,
14311526 name : str ,
@@ -1629,6 +1724,20 @@ def get_namespace_for_resource_import(
16291724
16301725 return entry .get_namespace ()
16311726
1727+ def get_resource_doc_for_resource_import (
1728+ self ,
1729+ name : str ,
1730+ base_dir : str ,
1731+ sentinel : Any = None ,
1732+ variables : Optional [Dict [str , Any ]] = None ,
1733+ * ,
1734+ source : Optional [str ] = None ,
1735+ ) -> ResourceDoc :
1736+ with self ._logger .measure_time (lambda : f"getting resource doc for { name } " , context_name = "import" ):
1737+ entry = self ._get_entry_for_resource_import (name , base_dir , sentinel , variables , source = source )
1738+
1739+ return entry .get_resource_doc ()
1740+
16321741 def get_libdoc_for_resource_import (
16331742 self ,
16341743 name : str ,
@@ -1637,10 +1746,10 @@ def get_libdoc_for_resource_import(
16371746 variables : Optional [Dict [str , Any ]] = None ,
16381747 * ,
16391748 source : Optional [str ] = None ,
1640- ) -> LibraryDoc :
1749+ ) -> ResourceDoc :
16411750 entry = self ._get_entry_for_resource_import (name , base_dir , sentinel , variables , source = source )
16421751
1643- return entry .get_libdoc ()
1752+ return entry .get_resource_doc ()
16441753
16451754 def complete_library_import (
16461755 self ,
0 commit comments