mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 20:15:56 +00:00
Remove _sanitize_symbol_dict, making to_dict fully flexible instead
This fixes a bug: _sanitize_symbol_dict was not recursively applied to children, causing children to contain superfluous entries (full location, etc.) Additional change: Improve factorisation of symbol overview computation
This commit is contained in:
1 parent
2760508269
commit
a1f95902ca
2 files changed
+60
-56
No files matched your search
+30
-30
@@ -396,9 +396,10 @@ class LanguageServerSymbol(Symbol, ToStringMixin):
|
||||
return result
|
||||
|
||||
class OutputDict(TypedDict):
|
||||
name: str
|
||||
name_path: str
|
||||
name_path: NotRequired[str]
|
||||
name: NotRequired[str]
|
||||
location: NotRequired[dict[str, Any]]
|
||||
relative_path: NotRequired[str | None]
|
||||
body_location: NotRequired[dict[str, Any]]
|
||||
body: NotRequired[str | None]
|
||||
kind: NotRequired[str]
|
||||
@@ -408,17 +409,23 @@ class LanguageServerSymbol(Symbol, ToStringMixin):
|
||||
|
||||
def to_dict(
|
||||
self,
|
||||
*,
|
||||
name_path: bool = True,
|
||||
name: bool = False,
|
||||
kind: bool = False,
|
||||
location: bool = False,
|
||||
depth: int = 0,
|
||||
include_body: bool = False,
|
||||
body_location: bool = False,
|
||||
include_children_body: bool = False,
|
||||
include_relative_path: bool = True,
|
||||
include_relative_path: bool = False,
|
||||
child_inclusion_predicate: Callable[[Self], bool] | None = None,
|
||||
) -> OutputDict:
|
||||
"""
|
||||
Converts the symbol to a dictionary.
|
||||
|
||||
:param name_path: whether to include the name path of the symbol
|
||||
:param name: whether to include the name of the symbol
|
||||
:param kind: whether to include the kind of the symbol
|
||||
:param location: whether to include the location of the symbol
|
||||
:param depth: the depth up to which to include child symbols (0 = do not include children)
|
||||
@@ -427,19 +434,30 @@ class LanguageServerSymbol(Symbol, ToStringMixin):
|
||||
Note that the body of the children is part of the body of the parent symbol,
|
||||
so there is usually no need to set this to True unless you want process the output
|
||||
and pass the children without passing the parent body to the LM.
|
||||
:param include_relative_path: whether to include the relative path of the symbol in the location
|
||||
entry. Relative paths of the symbol's children are always excluded.
|
||||
:param include_relative_path: whether to include the relative path of the symbol.
|
||||
If `location` is True, this defines whether to include the path in the location entry.
|
||||
If `location` is False, this defines whether to include the relative path as a top-level entry.
|
||||
Relative paths of the symbol's children are always excluded.
|
||||
:param child_inclusion_predicate: an optional predicate that decides whether a child symbol
|
||||
should be included.
|
||||
:return: a dictionary representation of the symbol
|
||||
"""
|
||||
result: LanguageServerSymbol.OutputDict = {"name": self.name, "name_path": self.get_name_path()}
|
||||
result: LanguageServerSymbol.OutputDict = {}
|
||||
|
||||
if name_path:
|
||||
result["name_path"] = self.get_name_path()
|
||||
if name:
|
||||
result["name"] = self.name
|
||||
|
||||
if kind:
|
||||
result["kind"] = self.kind
|
||||
|
||||
if location:
|
||||
result["location"] = self.location.to_dict(include_relative_path=include_relative_path)
|
||||
elif include_relative_path:
|
||||
result["relative_path"] = self.relative_path
|
||||
|
||||
if body_location:
|
||||
body_start_line, body_end_line = self.get_body_line_numbers()
|
||||
result["body_location"] = {"start_line": body_start_line, "end_line": body_end_line}
|
||||
|
||||
@@ -459,8 +477,11 @@ class LanguageServerSymbol(Symbol, ToStringMixin):
|
||||
continue
|
||||
children.append(
|
||||
c.to_dict(
|
||||
name_path=name_path,
|
||||
name=name,
|
||||
kind=kind,
|
||||
location=location,
|
||||
body_location=body_location,
|
||||
depth=depth - 1,
|
||||
child_inclusion_predicate=child_inclusion_predicate,
|
||||
include_body=include_children_body,
|
||||
@@ -700,36 +721,15 @@ class LanguageServerSymbolRetriever:
|
||||
|
||||
return [ReferenceInLanguageServerSymbol.from_lsp_reference(r) for r in references]
|
||||
|
||||
def get_symbol_overview(self, relative_path: str, depth: int = 0) -> dict[str, list[LanguageServerSymbol.OutputDict]]:
|
||||
def get_symbol_overview(self, relative_path: str) -> dict[str, list[LanguageServerSymbol]]:
|
||||
"""
|
||||
:param relative_path: the path of the file or directory for which to get the symbol overview
|
||||
:param depth: the depth up to which to include child symbols (0 = only top-level symbols)
|
||||
:return: a mapping from file paths to lists of symbol dictionaries.
|
||||
:return: a mapping from file paths to lists of symbols.
|
||||
For the case where a file is passed, the mapping will contain a single entry.
|
||||
"""
|
||||
lang_server = self.get_language_server(relative_path)
|
||||
path_to_unified_symbols = lang_server.request_overview(relative_path)
|
||||
|
||||
def child_inclusion_predicate(s: LanguageServerSymbol) -> bool:
|
||||
return not s.is_low_level()
|
||||
|
||||
result = {}
|
||||
for file_path, unified_symbols in path_to_unified_symbols.items():
|
||||
symbols_in_file = []
|
||||
for us in unified_symbols:
|
||||
symbol = LanguageServerSymbol(us)
|
||||
symbols_in_file.append(
|
||||
symbol.to_dict(
|
||||
depth=depth,
|
||||
kind=True,
|
||||
include_relative_path=False,
|
||||
location=False,
|
||||
child_inclusion_predicate=child_inclusion_predicate,
|
||||
)
|
||||
)
|
||||
result[file_path] = symbols_in_file
|
||||
|
||||
return result
|
||||
return {k: [LanguageServerSymbol(us) for us in v] for k, v in path_to_unified_symbols.items()}
|
||||
|
||||
|
||||
class JetBrainsSymbol(Symbol):
|
||||
|
||||
@@ -5,8 +5,6 @@ Language server-related tools
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from collections.abc import Sequence
|
||||
from copy import copy
|
||||
from typing import Any
|
||||
|
||||
from serena.symbol import LanguageServerSymbol, LanguageServerSymbolDictGrouper
|
||||
from serena.tools import (
|
||||
@@ -19,23 +17,6 @@ from serena.tools.tools_base import ToolMarkerOptional
|
||||
from solidlsp.ls_types import SymbolKind
|
||||
|
||||
|
||||
# TODO: We have to get rid of this.
|
||||
def _sanitize_symbol_dict(symbol_dict: LanguageServerSymbol.OutputDict) -> dict[str, Any]:
|
||||
"""
|
||||
Sanitize a symbol dictionary inplace by removing unnecessary information.
|
||||
"""
|
||||
# We replace the location entry, which repeats line information already included in body_location
|
||||
# and has unnecessary information on column, by just the relative path.
|
||||
symbol_dict = copy(symbol_dict)
|
||||
s_relative_path = symbol_dict.get("location", {}).get("relative_path")
|
||||
if s_relative_path is not None:
|
||||
symbol_dict["relative_path"] = s_relative_path
|
||||
symbol_dict.pop("location", None)
|
||||
# also remove name, name_path should be enough
|
||||
symbol_dict.pop("name")
|
||||
return symbol_dict
|
||||
|
||||
|
||||
class RestartLanguageServerTool(Tool, ToolMarkerOptional):
|
||||
"""Restarts the language server, may be necessary when edits not through Serena happen."""
|
||||
|
||||
@@ -69,8 +50,8 @@ class GetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead):
|
||||
:return: a JSON object containing symbols grouped by kind in a compact format.
|
||||
"""
|
||||
result = self.get_symbol_overview(relative_path, depth=depth)
|
||||
# compact_result = self._transform_symbols_to_compact_format(result)
|
||||
compact_result = self.symbol_dict_grouper.group(result)
|
||||
compact_result = self._transform_symbols_to_compact_format(result)
|
||||
# compact_result = self.symbol_dict_grouper.group(result)
|
||||
result_json_str = self._to_json(compact_result)
|
||||
return self._limit_length(result_json_str, max_answer_chars)
|
||||
|
||||
@@ -90,10 +71,28 @@ class GetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead):
|
||||
if os.path.isdir(file_path):
|
||||
raise ValueError(f"Expected a file path, but got a directory path: {relative_path}. ")
|
||||
|
||||
return symbol_retriever.get_symbol_overview(relative_path, depth=depth)[relative_path]
|
||||
symbols = symbol_retriever.get_symbol_overview(relative_path)[relative_path]
|
||||
|
||||
def child_inclusion_predicate(s: LanguageServerSymbol) -> bool:
|
||||
return not s.is_low_level()
|
||||
|
||||
symbol_dicts = []
|
||||
for symbol in symbols:
|
||||
symbol_dicts.append(
|
||||
symbol.to_dict(
|
||||
name_path=False,
|
||||
name=True,
|
||||
depth=depth,
|
||||
kind=True,
|
||||
include_relative_path=False,
|
||||
location=False,
|
||||
child_inclusion_predicate=child_inclusion_predicate,
|
||||
)
|
||||
)
|
||||
return symbol_dicts
|
||||
|
||||
@staticmethod
|
||||
def _transform_symbols_to_compact_format(symbols: list[dict[str, Any]]) -> dict[str, list]:
|
||||
def _transform_symbols_to_compact_format(symbols: list[LanguageServerSymbol.OutputDict]) -> dict[str, list]:
|
||||
"""
|
||||
Transform symbol overview from verbose format to compact grouped format.
|
||||
|
||||
@@ -190,7 +189,10 @@ class FindSymbolTool(Tool, ToolMarkerSymbolicRead):
|
||||
substring_matching=substring_matching,
|
||||
within_relative_path=relative_path,
|
||||
)
|
||||
symbol_dicts = [_sanitize_symbol_dict(s.to_dict(kind=True, location=True, depth=depth, include_body=include_body)) for s in symbols]
|
||||
symbol_dicts = [
|
||||
dict(s.to_dict(kind=True, include_relative_path=True, body_location=True, depth=depth, include_body=include_body))
|
||||
for s in symbols
|
||||
]
|
||||
if not include_body and include_info:
|
||||
# we add an info field to the symbol dicts if requested
|
||||
for s, s_dict in zip(symbols, symbol_dicts, strict=True):
|
||||
@@ -243,8 +245,10 @@ class FindReferencingSymbolsTool(Tool, ToolMarkerSymbolicRead):
|
||||
)
|
||||
reference_dicts = []
|
||||
for ref in references_in_symbols:
|
||||
ref_dict = ref.symbol.to_dict(kind=True, location=True, depth=0, include_body=include_body)
|
||||
ref_dict = _sanitize_symbol_dict(ref_dict)
|
||||
ref_dict_orig = ref.symbol.to_dict(
|
||||
kind=True, include_relative_path=True, depth=0, include_body=include_body, body_location=True
|
||||
)
|
||||
ref_dict = dict(ref_dict_orig)
|
||||
if not include_body:
|
||||
ref_relative_path = ref.symbol.location.relative_path
|
||||
assert ref_relative_path is not None, f"Referencing symbol {ref.symbol.name} has no relative path, this is likely a bug."
|
||||
|
||||
Reference in new issue
Block a user