diff --git a/CHANGELOG.md b/CHANGELOG.md index f1227c72..5e0722fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Status of the `main` branch. Changes prior to the next official version change w rather than guessing at a body that could be wrong #1498 * Language Servers: + - Fix: Properly differentiate between raw and high-level symbol cache fingerprints, avoiding unnecessary + invalidations of the raw cache when only the derived high-level representation changes - Java (JDT-LS): add `runtimes` to `ls_specific_settings.java`, a list of extra JRE/JDK entries (`name`, `path`, optional `default`/`sources`/`javadoc`) passed through to JDT-LS's `java.configuration.runtimes`. Fixes silently broken JDK type resolution (`java.lang.Object` diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index 4db49125..4633298f 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -78,7 +78,7 @@ class ClangdLanguageServer(SolidLanguageServer): self.resolve_main_method_available = threading.Event() @override - def _document_symbols_cache_fingerprint(self) -> Hashable: + def _raw_document_symbols_cache_fingerprint(self) -> Hashable: cache_format_version = 1 cpp_settings = self._custom_settings or {} return ( diff --git a/src/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index aa29ca16..8ebd7f38 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -169,12 +169,12 @@ class Gopls(SolidLanguageServer): @override def _document_symbols_cache_fingerprint(self) -> Hashable: - """ - Compute a deterministic fingerprint of method/implementation versions and Gopls settings/the Go build context. - """ normalize_symbol_name_version = 1 request_document_symbols_impl_version = 2 + return normalize_symbol_name_version, request_document_symbols_impl_version + @override + def _raw_document_symbols_cache_fingerprint(self) -> Hashable: gopls_settings_raw = self._custom_settings.settings.get("gopls_settings") gopls_settings: dict | None @@ -193,7 +193,7 @@ class Gopls(SolidLanguageServer): # Version processed symbols even when the build context itself is empty. if gopls_settings is None and not env_subset: - additional_settings_fingerprint = "" + return None else: fingerprint_data: dict[str, object] = { "env": env_subset, @@ -201,9 +201,7 @@ class Gopls(SolidLanguageServer): if gopls_settings is not None: fingerprint_data["gopls_settings"] = gopls_settings canonical_json = self._canonical_json_or_raise(json, fingerprint_data) - additional_settings_fingerprint = hashlib.sha256(canonical_json.encode("utf-8")).hexdigest()[:16] - - return normalize_symbol_name_version, request_document_symbols_impl_version, additional_settings_fingerprint + return hashlib.sha256(canonical_json.encode("utf-8")).hexdigest()[:16] @override def _normalize_symbol_name(self, symbol: RawDocumentSymbol, relative_file_path: str) -> str: diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 8b05a32f..820d241d 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -324,6 +324,12 @@ class SolidLanguageServer(ABC): 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 = 4 + """ + defines the version of the high-level document symbol format. + This should be incremented whenever there is a change in the way document symbols are stored. + For changes that affect the symbols of a particular language server implementation (subclass), + change :meth:`_document_symbols_cache_fingerprint` instead. + """ DOCUMENT_SYMBOL_CACHE_FILENAME = "document_symbols.pkl" # Directories that should always be ignored regardless of language: @@ -2891,13 +2897,14 @@ class SolidLanguageServer(ABC): Returns a fingerprint of any language server-specific aspects that result in changes to the high-level document symbol information. - Language servers must implement this method/change the return value - * whenever they change the `request_document_symbols` implementation to modify the returned content - * are reconfigured in a way that affects the returned contents (e.g. context-specific configuration - such as build flags or environment variables); configuration options can, in such cases, be - hashed together to produce a single fingerprint value. + Language servers must implement this method/change the return value if + the `request_document_symbols` implementation (or any of the methods called by it) + are changed to modify the returned content. - Whenever the value changes, the document symbols cache will be invalidated and re-populated. + Whenever the value changes, the high-level document symbols cache will be invalidated and re-populated. + + Note: For aspects that change the values returned by the language server itself, + change `_raw_document_symbols_cache_fingerprint` instead. The value must be hashable and safe for inclusion in cache version tuples. E.g. use an integer, a string or a tuple of integers/strings. @@ -2912,12 +2919,16 @@ class SolidLanguageServer(ABC): """ Return the version for the document symbols cache. - Incorporates cache context fingerprint if provided by the language server. + Incorporates cache fingerprints if provided by the language server. """ - fingerprint = self._document_symbols_cache_fingerprint() - if fingerprint is not None: - return (self.DOCUMENT_SYMBOL_CACHE_VERSION, fingerprint) - return self.DOCUMENT_SYMBOL_CACHE_VERSION + version: list[Hashable] = [self.DOCUMENT_SYMBOL_CACHE_VERSION] + high_level_fingerprint = self._document_symbols_cache_fingerprint() + if high_level_fingerprint is not None: + version.append(high_level_fingerprint) + raw_fingerprint = self._raw_document_symbols_cache_fingerprint() + if raw_fingerprint is not None: + version.append(raw_fingerprint) + return version[0] if len(version) == 1 else tuple(version) def _save_raw_document_symbols_cache(self) -> None: cache_file = self.cache_dir / self.RAW_DOCUMENT_SYMBOL_CACHE_FILENAME @@ -2937,9 +2948,32 @@ class SolidLanguageServer(ABC): e, ) - def _raw_document_symbols_cache_version(self) -> tuple[Hashable, ...]: - base_version: tuple[Hashable, ...] = (self.RAW_DOCUMENT_SYMBOLS_CACHE_VERSION, self._ls_specific_raw_document_symbols_cache_version) - fingerprint = self._document_symbols_cache_fingerprint() + def _raw_document_symbols_cache_fingerprint(self) -> Hashable | None: + """ + Returns a fingerprint of any language server-specific aspects that result in changes + to the raw document symbol information produced by the language server. + + Language servers must implement this method/change the return value whenever they + are reconfigured or otherwise affected in a way that affects the contents returned + by the language server for document symbol requests (raw request result). + For example, this could be context-specific configuration such as build flags or environment variables; + configuration options can, in such cases, be hashed together to produce a single fingerprint value. + + Whenever the value changes, the raw document symbols cache (as well as the high-level cache that + builds on the raw results) will be invalidated and re-populated. + + The value must be hashable and safe for inclusion in cache version tuples. + E.g. use an integer, a string or a tuple of integers/strings. + + For example, if there is a single aspect being considered, use an integer to reflect the version + of this aspect (incrementing it whenever the implementation changes). + If multiple versioned aspects exist, use a tuple of versions, etc. + """ + return None + + def _raw_document_symbols_cache_version(self) -> Hashable: + base_version = (self.RAW_DOCUMENT_SYMBOLS_CACHE_VERSION, self._ls_specific_raw_document_symbols_cache_version) + fingerprint = self._raw_document_symbols_cache_fingerprint() if fingerprint is not None: return (*base_version, fingerprint) return base_version