Merge pull request #446 from oraios/refactor/symbol-overview-data-structure

This commit is contained in:
Dominik Jain authored and GitHub committed 2025-08-08 14:11:18 +02:00
commit f106572b74
3 files changed
+28 -44

No files matched your search

+7 -3
View File
@@ -584,13 +584,17 @@ class LanguageServerSymbolRetriever:
name_path: str
kind: int
@classmethod
def from_symbol(cls, symbol: LanguageServerSymbol) -> Self:
return cls(name_path=symbol.get_name_path(), kind=int(symbol.symbol_kind))
def get_symbol_overview(self, relative_path: str) -> dict[str, list[SymbolOverviewElement]]:
path_to_symbol_infos = self._lang_server.request_overview(relative_path)
path_to_unified_symbols = self._lang_server.request_overview(relative_path)
result = {}
for file_path, symbols in path_to_symbol_infos.items():
for file_path, unified_symbols in path_to_unified_symbols.items():
# TODO: maybe include not just top-level symbols? We could filter by kind to exclude variables
# The language server methods would need to be adjusted for this.
result[file_path] = [self.SymbolOverviewElement(name_path=symbol[0], kind=int(symbol[1])) for symbol in symbols]
result[file_path] = [self.SymbolOverviewElement.from_symbol(LanguageServerSymbol(s)) for s in unified_symbols]
return result
+10 -30
View File
@@ -25,6 +25,7 @@ from solidlsp.ls_config import Language, LanguageServerConfig
from solidlsp.ls_exceptions import SolidLSPException
from solidlsp.ls_handler import SolidLanguageServerHandler
from solidlsp.ls_logger import LanguageServerLogger
from solidlsp.ls_types import UnifiedSymbolInformation
from solidlsp.ls_utils import FileUtils, PathUtils, TextUtils
from solidlsp.lsp_protocol_handler import lsp_types
from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes
@@ -1165,33 +1166,21 @@ class SolidLanguageServer(ABC):
end_column = len(lines[-1])
return ls_types.Range(start=ls_types.Position(line=0, character=0), end=ls_types.Position(line=end_line, character=end_column))
def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]]:
def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[UnifiedSymbolInformation]]:
"""
An overview of the given directory.
Maps relative paths of all contained files to info about top-level symbols in the file
(name, kind, line, column).
:return: A mapping of all relative paths analyzed to lists of top-level symbols in the corresponding file.
"""
symbol_tree = self.request_full_symbol_tree(relative_dir_path)
# Initialize result dictionary
result: dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]] = defaultdict(list)
result: dict[str, list[UnifiedSymbolInformation]] = defaultdict(list)
# Helper function to process a symbol and its children
def process_symbol(symbol: ls_types.UnifiedSymbolInformation):
if symbol["kind"] == ls_types.SymbolKind.File:
# For file symbols, process their children (top-level symbols)
for child in symbol["children"]:
assert "location" in child
assert "selectionRange" in child
path = Path(child["location"]["absolutePath"]).resolve().relative_to(self.repository_root_path)
result[str(path)].append(
(
child["name"],
child["kind"],
child["selectionRange"]["start"]["line"],
child["selectionRange"]["start"]["character"],
)
)
result[str(path)].append(child)
# For package/directory symbols, process their children
for child in symbol["children"]:
process_symbol(child)
@@ -1201,28 +1190,19 @@ class SolidLanguageServer(ABC):
process_symbol(root)
return result
def request_document_overview(self, relative_file_path: str) -> list[tuple[str, ls_types.SymbolKind, int, int]]:
def request_document_overview(self, relative_file_path: str) -> list[UnifiedSymbolInformation]:
"""
An overview of the given file.
Returns the list of tuples (name, kind, line, column) of all top-level symbols in the file.
:return: the top-level symbols in the given file.
"""
_, document_roots = self.request_document_symbols(relative_file_path)
result = []
for root in document_roots:
try:
result.append(
(root["name"], root["kind"], root["selectionRange"]["start"]["line"], root["selectionRange"]["start"]["character"])
)
except KeyError as e:
raise KeyError(f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}") from e
return result
return document_roots
def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]]:
def request_overview(self, within_relative_path: str) -> dict[str, list[UnifiedSymbolInformation]]:
"""
An overview of all symbols in the given file or directory.
:param within_relative_path: the relative path to the file or directory to get the overview of.
:return: A mapping of all relative paths analyzed to lists of tuples (name, kind, line, column) of all top-level symbols in the corresponding file.
:return: A mapping of all relative paths analyzed to lists of top-level symbols in the corresponding file.
"""
abs_path = (Path(self.repository_root_path) / within_relative_path).resolve()
if not abs_path.exists():
+11 -11
View File
@@ -10,6 +10,7 @@ import os
import pytest
from serena.symbol import LanguageServerSymbol
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
@@ -427,16 +428,15 @@ class TestLanguageServerSymbols:
assert len(services_symbols) > 0
# Check for specific symbols from services.py
expected_symbols = [
("UserService", SymbolKind.Class, 9, 6),
("ItemService", SymbolKind.Class, 40, 6),
("create_service_container", SymbolKind.Function, 67, 4),
("user_var_str", SymbolKind.Variable, 73, 0),
("user_service", SymbolKind.Variable, 76, 0),
]
for symbol in expected_symbols:
assert symbol in services_symbols
expected_symbols = {
"UserService",
"ItemService",
"create_service_container",
"user_var_str",
"user_service",
}
retrieved_symbols = {symbol["name"] for symbol in services_symbols if "name" in symbol}
assert expected_symbols.issubset(retrieved_symbols)
@pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True)
def test_request_document_overview(self, language_server: SolidLanguageServer) -> None:
@@ -445,7 +445,7 @@ class TestLanguageServerSymbols:
overview = language_server.request_document_overview(os.path.join("examples", "user_management.py"))
# Verify that we have entries for both files
symbol_names = {s_info[0] for s_info in overview}
symbol_names = {LanguageServerSymbol(s_info).name for s_info in overview}
assert {"UserStats", "UserManager", "process_user_data", "main"}.issubset(symbol_names)
@pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True)