mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 18:16:43 +00:00
SolidLanguageServer: Support name normalisation in request_document_symbols via new method
* Introduce method _normalize_symbol_name to support name transformation * Apply in CSharpLanguageServer, fixing old override of request_document_symbols not accounting for overload handling (invalidating high-level cache accordingly) * Apply in ALLanguageServer
This commit is contained in:
1 parent
c184462938
commit
97a6845c78
3 files changed
+71
-72
No files matched your search
@@ -18,7 +18,7 @@ from overrides import override
|
||||
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.language_servers.common import quote_windows_path
|
||||
from solidlsp.ls import DocumentSymbols, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls import DocumentSymbols, GenericDocumentSymbol, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_types import SymbolKind, UnifiedSymbolInformation
|
||||
from solidlsp.ls_utils import FileUtils
|
||||
@@ -1007,35 +1007,25 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
# Get symbols from parent implementation
|
||||
document_symbols = super().request_document_symbols(relative_file_path, file_buffer=file_buffer)
|
||||
|
||||
# Normalize names by stripping AL object metadata, storing originals for hover
|
||||
def normalize_name(symbol: UnifiedSymbolInformation) -> None:
|
||||
original_name = symbol["name"]
|
||||
normalized_name = self._extract_al_display_name(original_name)
|
||||
|
||||
# Store original name if it was normalized (for hover injection)
|
||||
# Only store if we have valid position data to avoid false matches at (0, 0)
|
||||
if original_name != normalized_name:
|
||||
sel_range = symbol.get("selectionRange")
|
||||
if sel_range:
|
||||
start = sel_range.get("start")
|
||||
if start and "line" in start and "character" in start:
|
||||
line = start["line"]
|
||||
char = start["character"]
|
||||
self._al_original_names[(relative_file_path, line, char)] = original_name
|
||||
|
||||
symbol["name"] = normalized_name
|
||||
|
||||
# Process children recursively
|
||||
if symbol.get("children"):
|
||||
for child in symbol["children"]:
|
||||
normalize_name(child)
|
||||
|
||||
# Apply to all root symbols
|
||||
for sym in document_symbols.root_symbols:
|
||||
normalize_name(sym)
|
||||
|
||||
return document_symbols
|
||||
|
||||
def _normalize_symbol_name(self, symbol: GenericDocumentSymbol, relative_file_path: str) -> str:
|
||||
original_name = symbol["name"]
|
||||
normalized_name = self._extract_al_display_name(original_name)
|
||||
|
||||
# Store original name if it was normalized (for hover injection)
|
||||
# Only store if we have valid position data to avoid false matches at (0, 0)
|
||||
if original_name != normalized_name:
|
||||
sel_range = symbol.get("selectionRange")
|
||||
if sel_range:
|
||||
start = sel_range.get("start") # type: ignore
|
||||
if start and "line" in start and "character" in start:
|
||||
line = start["line"]
|
||||
char = start["character"]
|
||||
self._al_original_names[(relative_file_path, line, char)] = original_name
|
||||
|
||||
return normalized_name
|
||||
|
||||
@override
|
||||
def request_hover(
|
||||
self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None
|
||||
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
import platform
|
||||
import shutil
|
||||
import threading
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Hashable, Iterable
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
@@ -15,10 +15,15 @@ from typing import Any, cast
|
||||
from overrides import override
|
||||
|
||||
from serena.util.dotnet import DotNETUtil
|
||||
from solidlsp.ls import DocumentSymbols, LanguageServerDependencyProvider, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls import (
|
||||
GenericDocumentSymbol,
|
||||
LanguageServerDependencyProvider,
|
||||
LSPFileBuffer,
|
||||
SolidLanguageServer,
|
||||
)
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_types import Hover, UnifiedSymbolInformation
|
||||
from solidlsp.ls_types import Hover
|
||||
from solidlsp.ls_utils import FileUtils, PathUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, InitializeResult
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
@@ -218,28 +223,6 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj", "packages", ".vs"]
|
||||
|
||||
@override
|
||||
def request_document_symbols(self, relative_file_path: str, file_buffer: Any = None) -> DocumentSymbols:
|
||||
"""
|
||||
Override to normalize Roslyn symbol names and cache originals.
|
||||
|
||||
Roslyn 5.5.0+ returns symbol names with type annotations:
|
||||
- Properties: "Name : string"
|
||||
- Methods: "Add(int, int) : int"
|
||||
|
||||
This method:
|
||||
1. Normalizes names to base form ("Name", "Add")
|
||||
2. Caches original names for rich information display
|
||||
3. Populates LSP spec's 'detail' field with type/signature info
|
||||
"""
|
||||
symbols = super().request_document_symbols(relative_file_path, file_buffer)
|
||||
|
||||
# Normalize all symbols recursively
|
||||
for symbol in symbols.iter_symbols():
|
||||
self._normalize_symbol_name(symbol, relative_file_path)
|
||||
|
||||
return symbols
|
||||
|
||||
@override
|
||||
def request_hover(self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None) -> Hover | None:
|
||||
"""
|
||||
@@ -265,12 +248,20 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
|
||||
return hover
|
||||
|
||||
def _normalize_symbol_name(self, symbol: UnifiedSymbolInformation, relative_file_path: str) -> None:
|
||||
"""
|
||||
Normalize a single symbol's name and cache the original.
|
||||
Processes children recursively.
|
||||
"""
|
||||
original_name = symbol.get("name", "")
|
||||
def _document_symbols_cache_fingerprint(self) -> Hashable | None:
|
||||
normalize_symbol_name_version = 1
|
||||
return normalize_symbol_name_version
|
||||
|
||||
def _normalize_symbol_name(self, symbol: GenericDocumentSymbol, relative_file_path: str) -> str:
|
||||
# Roslyn 5.5.0+ returns symbol names with type annotations:
|
||||
# - Properties: "Name : string"
|
||||
# - Methods: "Add(int, int) : int"
|
||||
#
|
||||
# This method:
|
||||
# 1. Normalizes names to base form ("Name", "Add")
|
||||
# 2. Caches original names for rich information display
|
||||
# 3. Populates LSP spec's 'detail' field with type/signature info
|
||||
original_name = symbol["name"]
|
||||
|
||||
# Extract base name and type/signature info
|
||||
normalized_name, type_info = self._extract_base_name_and_type(original_name)
|
||||
@@ -279,24 +270,18 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
if original_name != normalized_name:
|
||||
sel_range = symbol.get("selectionRange")
|
||||
if sel_range:
|
||||
start = sel_range.get("start")
|
||||
start = sel_range.get("start") # type: ignore
|
||||
if start and "line" in start and "character" in start:
|
||||
line = start["line"]
|
||||
char = start["character"]
|
||||
cache_key = (relative_file_path, line, char)
|
||||
self._original_symbol_names[cache_key] = original_name
|
||||
|
||||
# Populate LSP spec's 'detail' field with type/signature information
|
||||
# Populate 'detail' field with type/signature information (for UnifiedSymbolInformation)
|
||||
if type_info and "detail" not in symbol:
|
||||
symbol["detail"] = type_info
|
||||
symbol["detail"] = type_info # type: ignore
|
||||
|
||||
# Update the symbol name
|
||||
symbol["name"] = normalized_name
|
||||
|
||||
# Process children recursively
|
||||
children = symbol.get("children", [])
|
||||
for child in children:
|
||||
self._normalize_symbol_name(child, relative_file_path)
|
||||
return normalized_name
|
||||
|
||||
@staticmethod
|
||||
def _extract_base_name_and_type(roslyn_name: str) -> tuple[str, str]:
|
||||
|
||||
+27
-3
@@ -1262,7 +1262,7 @@ class SolidLanguageServer(ABC):
|
||||
The returned symbols are considered "raw document symbols" (in contrast to processed symbols returned by `request_document_symbols`).
|
||||
|
||||
NOTE: This method can be overridden in subclasses to post-process the raw results.
|
||||
When doing so, be sure to update the init parameter `cache_version_raw_document_symbols`
|
||||
When doing so after the initial implementation, be sure to update the init parameter `cache_version_raw_document_symbols`
|
||||
to a different version (add 1) to ensure that all caches are invalidated appropriately.
|
||||
IMPORTANT: Since rebuilding the raw document symbol cache from the language server results
|
||||
is potentially expensive, prefer overriding the `request_document_symbols` method
|
||||
@@ -1312,13 +1312,30 @@ class SolidLanguageServer(ABC):
|
||||
with self._open_file_context(relative_file_path, file_buffer=file_data) as fd:
|
||||
return get_raw_document_symbols(fd)
|
||||
|
||||
def _normalize_symbol_name(self, symbol: GenericDocumentSymbol, relative_file_path: str) -> str:
|
||||
"""
|
||||
Normalizes the name of the given symbol, e.g. by removing parameter lists from method symbols.
|
||||
|
||||
Override this method in subclasses to implement language-specific normalization logic.
|
||||
NOTE: When changing the override of this method after the initial LS implementation,
|
||||
be sure to also override `_document_symbols_cache_fingerprint` in order to ensure that
|
||||
the caches are invalidated appropriately.
|
||||
|
||||
:param symbol: the symbol
|
||||
:param relative_file_path: the relative path of the file the symbol is located in
|
||||
:return: the normalized name of the symbol
|
||||
"""
|
||||
# the default implementation does not change the name
|
||||
return symbol["name"]
|
||||
|
||||
def request_document_symbols(self, relative_file_path: str, file_buffer: LSPFileBuffer | None = None) -> DocumentSymbols:
|
||||
"""
|
||||
Retrieves the collection of symbols in the given file.
|
||||
|
||||
NOTE: This method can be overridden in subclasses to post-process the results.
|
||||
When doing so, be sure to also override `_document_symbols_cache_fingerprint`
|
||||
When doing so after the initial LS implementation, be sure to also override `_document_symbols_cache_fingerprint`
|
||||
to ensure that the caches are invalidated appropriately.
|
||||
DO NOT override this method to modify symbol names; override `_normalize_symbol_name` instead.
|
||||
|
||||
:param relative_file_path: The relative path of the file that has the symbols
|
||||
:param file_buffer: an optional file buffer if the file is already opened.
|
||||
@@ -1410,9 +1427,16 @@ class SolidLanguageServer(ABC):
|
||||
Converts the given symbols into UnifiedSymbolInformation with proper parent-child relationships,
|
||||
adding overload indices for symbols with the same name under the same parent.
|
||||
"""
|
||||
# apply name normalization and count occurrences of each symbol name
|
||||
total_name_counts: dict[str, int] = defaultdict(lambda: 0)
|
||||
for symbol in symbols:
|
||||
total_name_counts[symbol["name"]] += 1
|
||||
name = self._normalize_symbol_name(symbol, relative_file_path=relative_file_path)
|
||||
symbol["name"] = name
|
||||
total_name_counts[name] += 1
|
||||
|
||||
# convert symbols to the unified representation and
|
||||
# * add overload indices where necessary
|
||||
# * ensure that the "parent" field is set correctly
|
||||
name_counts: dict[str, int] = defaultdict(lambda: 0)
|
||||
unified_symbols = []
|
||||
for symbol in symbols:
|
||||
|
||||
Reference in new issue
Block a user