mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 08:15:59 +00:00
Merge pull request #446 from oraios/refactor/symbol-overview-data-structure
This commit is contained in:
3 files changed
+28
-44
No files matched your search
@@ -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
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user