mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 04:15:11 +00:00
Reintroduce (high-level) document symbols cache, now storing instance of DocumentSymbols
This commit is contained in:
1 parent
d8cac3aedb
commit
7af490d0a2
1 file changed
+89
-50
+89
-50
@@ -127,10 +127,14 @@ class SolidLanguageServer(ABC):
|
||||
"""
|
||||
|
||||
CACHE_FOLDER_NAME = "cache"
|
||||
CACHE_VERSION_RAW_DOCUMENT_SYMBOLS = 1
|
||||
CACHE_FILE_RAW_DOCUMENT_SYMBOLS = "raw_document_symbols.pkl"
|
||||
CACHE_FILE_RAW_DOCUMENT_SYMBOLS_LEGACY = "document_symbols_cache_v23-06-25.pkl"
|
||||
CACHE_VERSION = 2
|
||||
RAW_DOCUMENT_SYMBOLS_CACHE_VERSION = 1
|
||||
"""
|
||||
global version identifier for raw symbol caches; an LS-specific version is defined separately and combined with this
|
||||
"""
|
||||
RAW_DOCUMENT_SYMBOL_CACHE_FILENAME = "raw_document_symbols.pkl"
|
||||
RAW_DOCUMENT_SYMBOL_CACHE_FILENAME_LEGACY_FALLBACK = "document_symbols_cache_v23-06-25.pkl"
|
||||
DOCUMENT_SYMBOL_CACHE_VERSION = 3
|
||||
DOCUMENT_SYMBOL_CACHE_FILENAME = "document_symbols.pkl"
|
||||
|
||||
# To be overridden and extended by subclasses
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
@@ -239,16 +243,22 @@ class SolidLanguageServer(ABC):
|
||||
self.open_file_buffers: dict[str, LSPFileBuffer] = {}
|
||||
self.language = Language(language_id)
|
||||
|
||||
# load caches
|
||||
# initialise symbol caches
|
||||
self._cache_dir = (
|
||||
Path(self.repository_root_path) / self._solidlsp_settings.project_data_relative_path / self.CACHE_FOLDER_NAME / self.language_id
|
||||
)
|
||||
self._cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
self._raw_document_symbols_cache_version = cache_version_raw_document_symbols
|
||||
# * raw document symbols cache
|
||||
self._ls_specific_raw_document_symbols_cache_version = cache_version_raw_document_symbols
|
||||
self._raw_document_symbols_cache: dict[str, tuple[str, list[GenericDocumentSymbol]]] = {}
|
||||
"""Maps relative file paths to a tuple of (file_content_hash, root_symbols)"""
|
||||
"""maps relative file paths to a tuple of (file_content_hash, raw_root_symbols)"""
|
||||
self._raw_document_symbols_cache_is_modified: bool = False
|
||||
self._load_raw_document_symbols_cache()
|
||||
# * high-level document symbols cache
|
||||
self._document_symbols_cache: dict[str, tuple[str, DocumentSymbols]] = {}
|
||||
"""maps relative file paths to a tuple of (file_content_hash, document_symbols)"""
|
||||
self._document_symbols_cache_is_modified: bool = False
|
||||
self._load_document_symbols_cache()
|
||||
|
||||
self.server_started = False
|
||||
self.completions_available = threading.Event()
|
||||
@@ -898,12 +908,12 @@ class SolidLanguageServer(ABC):
|
||||
if file_hash_and_result is not None:
|
||||
file_hash, result = file_hash_and_result
|
||||
if file_hash == fd.content_hash:
|
||||
log.debug("Returning cached document symbols for %s", relative_file_path)
|
||||
log.debug("Returning cached raw document symbols for %s", relative_file_path)
|
||||
return result
|
||||
else:
|
||||
log.debug("Content for %s has changed. Will overwrite in-memory cache", relative_file_path)
|
||||
log.debug("Raw document symbol content for %s has changed. Will overwrite in-memory cache", relative_file_path)
|
||||
else:
|
||||
log.debug("No cache hit for symbols in %s", relative_file_path)
|
||||
log.debug("No cache hit for raw document symbols symbols in %s", relative_file_path)
|
||||
return None
|
||||
|
||||
def get_symbols(fd: LSPFileBuffer) -> list[GenericDocumentSymbol]:
|
||||
@@ -945,38 +955,32 @@ class SolidLanguageServer(ABC):
|
||||
If you need a symbol tree that contains file symbols as well, you should use `request_full_symbol_tree` instead.
|
||||
"""
|
||||
# check if the desired result is cached
|
||||
cache_key = relative_file_path
|
||||
with self.open_file(relative_file_path) as file_data:
|
||||
"""
|
||||
cache_key = f"{relative_file_path}-{include_body}"
|
||||
with self._cache_lock:
|
||||
file_hash_and_result = self._raw_document_symbols_cache.get(cache_key)
|
||||
if file_hash_and_result is not None:
|
||||
file_hash, result = file_hash_and_result
|
||||
if file_hash == file_data.content_hash:
|
||||
self.logger.log(f"Returning cached document symbols for {relative_file_path}", logging.DEBUG)
|
||||
return result
|
||||
else:
|
||||
self.logger.log(f"Content for {relative_file_path} has changed. Will overwrite in-memory cache", logging.DEBUG)
|
||||
file_hash_and_result = self._document_symbols_cache.get(cache_key)
|
||||
if file_hash_and_result is not None:
|
||||
file_hash, document_symbols = file_hash_and_result
|
||||
if file_hash == file_data.content_hash:
|
||||
log.debug("Returning cached document symbols for %s", relative_file_path)
|
||||
return document_symbols
|
||||
else:
|
||||
self.logger.log(f"No cache hit for symbols with {include_body=} in {relative_file_path}", logging.DEBUG)
|
||||
"""
|
||||
log.debug("Cached document symbol content for %s has changed", relative_file_path)
|
||||
else:
|
||||
log.debug("No cache hit for document symbols in %s", relative_file_path)
|
||||
|
||||
# request the symbols from the language server
|
||||
# no cached result: request the root symbols from the language server
|
||||
response = self._request_document_symbols(relative_file_path, file_data)
|
||||
|
||||
if response is None:
|
||||
self.logger.log(
|
||||
log.warning(
|
||||
f"Received None response from the Language Server for document symbols in {relative_file_path}. "
|
||||
f"This means the language server can't understand this file (possibly due to syntax errors). It may also be due to a bug or misconfiguration of the LS. "
|
||||
f"Returning empty list",
|
||||
logging.WARNING,
|
||||
)
|
||||
return [], []
|
||||
return DocumentSymbols([])
|
||||
|
||||
assert isinstance(response, list), f"Unexpected response from Language Server: {response}"
|
||||
self.logger.log(
|
||||
f"Received {len(response)} document symbols for {relative_file_path} from the Language Server",
|
||||
logging.DEBUG,
|
||||
)
|
||||
log.debug("Received %d root symbols for %s from the language server", len(response), relative_file_path)
|
||||
|
||||
def turn_item_into_unified_symbol(item: GenericDocumentSymbol) -> ls_types.UnifiedSymbolInformation:
|
||||
"""
|
||||
@@ -1042,17 +1046,14 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
root_nodes: list[ls_types.UnifiedSymbolInformation] = response
|
||||
convert_nodes_with_common_parent(root_nodes, None)
|
||||
result = DocumentSymbols(root_nodes)
|
||||
document_symbols = DocumentSymbols(root_nodes)
|
||||
|
||||
# update cache
|
||||
"""
|
||||
self.logger.log(f"Caching document symbols for {relative_file_path}", logging.DEBUG)
|
||||
with self._cache_lock:
|
||||
self._raw_document_symbols_cache[cache_key] = (file_data.content_hash, result)
|
||||
self._raw_document_symbols_cache_has_changed = True
|
||||
"""
|
||||
self._document_symbols_cache[cache_key] = (file_data.content_hash, document_symbols)
|
||||
self._document_symbols_cache_is_modified = True
|
||||
|
||||
return result
|
||||
return document_symbols
|
||||
|
||||
def request_full_symbol_tree(
|
||||
self, within_relative_path: str | None = None, include_body: bool = False
|
||||
@@ -1316,6 +1317,7 @@ class SolidLanguageServer(ABC):
|
||||
symbol_start_line = symbol["location"]["range"]["start"]["line"]
|
||||
symbol_end_line = symbol["location"]["range"]["end"]["line"]
|
||||
assert "relativePath" in symbol["location"]
|
||||
# TODO fixme: duplicate read
|
||||
symbol_file = self.retrieve_full_file_content(symbol["location"]["relativePath"])
|
||||
symbol_lines = symbol_file.split("\n")
|
||||
symbol_body = "\n".join(symbol_lines[symbol_start_line : symbol_end_line + 1])
|
||||
@@ -1649,8 +1651,7 @@ class SolidLanguageServer(ABC):
|
||||
return defining_symbol
|
||||
|
||||
def _save_raw_document_symbols_cache(self):
|
||||
cache_file = self._cache_dir / self.CACHE_FILE_RAW_DOCUMENT_SYMBOLS
|
||||
cache_version = (self.CACHE_VERSION_RAW_DOCUMENT_SYMBOLS, self._raw_document_symbols_cache_version)
|
||||
cache_file = self._cache_dir / self.RAW_DOCUMENT_SYMBOL_CACHE_FILENAME
|
||||
|
||||
if not self._raw_document_symbols_cache_is_modified:
|
||||
log.debug("No changes to raw document symbols cache, skipping save")
|
||||
@@ -1658,7 +1659,7 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
log.info("Saving updated raw document symbols cache to %s", cache_file)
|
||||
try:
|
||||
save_cache(str(cache_file), cache_version, self._raw_document_symbols_cache)
|
||||
save_cache(str(cache_file), self._raw_document_symbols_cache_version(), self._raw_document_symbols_cache)
|
||||
self._raw_document_symbols_cache_is_modified = False
|
||||
except Exception as e:
|
||||
log.error(
|
||||
@@ -1667,16 +1668,15 @@ class SolidLanguageServer(ABC):
|
||||
e,
|
||||
)
|
||||
|
||||
def save_cache(self):
|
||||
self._save_raw_document_symbols_cache()
|
||||
def _raw_document_symbols_cache_version(self):
|
||||
return (self.RAW_DOCUMENT_SYMBOLS_CACHE_VERSION, self._ls_specific_raw_document_symbols_cache_version)
|
||||
|
||||
def _load_raw_document_symbols_cache(self):
|
||||
cache_file = self._cache_dir / self.CACHE_FILE_RAW_DOCUMENT_SYMBOLS
|
||||
cache_version = (self.CACHE_VERSION_RAW_DOCUMENT_SYMBOLS, self._raw_document_symbols_cache_version)
|
||||
cache_file = self._cache_dir / self.RAW_DOCUMENT_SYMBOL_CACHE_FILENAME
|
||||
|
||||
if not cache_file.exists():
|
||||
# check for legacy cache to load to migrate
|
||||
legacy_cache_file = self._cache_dir / self.CACHE_FILE_RAW_DOCUMENT_SYMBOLS_LEGACY
|
||||
legacy_cache_file = self._cache_dir / self.RAW_DOCUMENT_SYMBOL_CACHE_FILENAME_LEGACY_FALLBACK
|
||||
if legacy_cache_file.exists():
|
||||
try:
|
||||
legacy_cache: dict[
|
||||
@@ -1698,20 +1698,59 @@ class SolidLanguageServer(ABC):
|
||||
return
|
||||
|
||||
# cache file exists, so load it
|
||||
log.info(f"Loading document symbols cache from %s", cache_file)
|
||||
log.info("Loading document symbols cache from %s", cache_file)
|
||||
try:
|
||||
saved_cache = load_cache(str(cache_file), cache_version)
|
||||
saved_cache = load_cache(str(cache_file), self._raw_document_symbols_cache_version())
|
||||
if saved_cache is not None:
|
||||
self._raw_document_symbols_cache = saved_cache
|
||||
self.logger.log(f"Loaded {len(self._raw_document_symbols_cache)} entries from document symbols cache.", logging.INFO)
|
||||
self.logger.log(f"Loaded {len(self._raw_document_symbols_cache)} entries from raw document symbols cache.", logging.INFO)
|
||||
except Exception as e:
|
||||
# cache can become corrupt, so just skip loading it
|
||||
log.warning(
|
||||
"Failed to load document symbols cache from %s (%s); Ignoring cache.",
|
||||
"Failed to load raw document symbols cache from %s (%s); Ignoring cache.",
|
||||
cache_file,
|
||||
e,
|
||||
)
|
||||
|
||||
def _save_document_symbols_cache(self):
|
||||
cache_file = self._cache_dir / self.DOCUMENT_SYMBOL_CACHE_FILENAME
|
||||
|
||||
if not self._document_symbols_cache_is_modified:
|
||||
log.debug("No changes to document symbols cache, skipping save")
|
||||
return
|
||||
|
||||
log.info("Saving updated document symbols cache to %s", cache_file)
|
||||
try:
|
||||
save_cache(str(cache_file), self.DOCUMENT_SYMBOL_CACHE_VERSION, self._document_symbols_cache)
|
||||
self._document_symbols_cache_is_modified = False
|
||||
except Exception as e:
|
||||
log.error(
|
||||
"Failed to save document symbols cache to %s: %s. Note: this may have resulted in a corrupted cache file.",
|
||||
cache_file,
|
||||
e,
|
||||
)
|
||||
|
||||
def _load_document_symbols_cache(self):
|
||||
cache_file = self._cache_dir / self.DOCUMENT_SYMBOL_CACHE_FILENAME
|
||||
if cache_file.exists():
|
||||
log.info("Loading document symbols cache from %s", cache_file)
|
||||
try:
|
||||
saved_cache = load_cache(str(cache_file), self.DOCUMENT_SYMBOL_CACHE_VERSION)
|
||||
if saved_cache is not None:
|
||||
self._document_symbols_cache = saved_cache
|
||||
self.logger.log(f"Loaded {len(self._document_symbols_cache)} entries from document symbols cache.", logging.INFO)
|
||||
except Exception as e:
|
||||
# cache can become corrupt, so just skip loading it
|
||||
log.warning(
|
||||
"Failed to load document symbols cache from %s (%s); Ignoring cache.",
|
||||
cache_file,
|
||||
e,
|
||||
)
|
||||
|
||||
def save_cache(self):
|
||||
self._save_raw_document_symbols_cache()
|
||||
self._save_document_symbols_cache()
|
||||
|
||||
def request_workspace_symbol(self, query: str) -> list[ls_types.UnifiedSymbolInformation] | None:
|
||||
"""
|
||||
Raise a [workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) request to the Language Server
|
||||
|
||||
Reference in new issue
Block a user