mirror of
https://github.com/tiennm99/serena.git
synced 2026-07-31 18:21:12 +00:00
Symbol: added find, insert, replace and delete by name. Also added delete by location.
Renamed name to name_path
This commit is contained in:
+4
-4
@@ -1202,7 +1202,7 @@ class FindReferencingSymbolsTool(Tool):
|
||||
"""
|
||||
parsed_include_kinds: Sequence[SymbolKind] | None = [SymbolKind(k) for k in include_kinds] if include_kinds else None
|
||||
parsed_exclude_kinds: Sequence[SymbolKind] | None = [SymbolKind(k) for k in exclude_kinds] if exclude_kinds else None
|
||||
symbols = self.symbol_manager.find_referencing_symbols(
|
||||
symbols = self.symbol_manager.find_referencing_symbols_by_location(
|
||||
SymbolLocation(relative_path, line, column),
|
||||
include_body=include_body,
|
||||
include_kinds=parsed_include_kinds,
|
||||
@@ -1279,7 +1279,7 @@ class ReplaceSymbolBodyTool(Tool, ToolMarkerCanEdit):
|
||||
:param body: the new symbol body. Important: Provide the correct level of indentation
|
||||
(as the original body). Note that the first line must not be indented (i.e. no leading spaces).
|
||||
"""
|
||||
self.symbol_manager.replace_body(
|
||||
self.symbol_manager.replace_body_at_location(
|
||||
SymbolLocation(relative_path, line, column),
|
||||
body=body,
|
||||
)
|
||||
@@ -1308,7 +1308,7 @@ class InsertAfterSymbolTool(Tool, ToolMarkerCanEdit):
|
||||
:param body: the body/content to be inserted
|
||||
"""
|
||||
location = SymbolLocation(relative_path, line, column)
|
||||
self.symbol_manager.insert_after(
|
||||
self.symbol_manager.insert_after_symbol_at_location(
|
||||
location,
|
||||
body=body,
|
||||
)
|
||||
@@ -1337,7 +1337,7 @@ class InsertBeforeSymbolTool(Tool, ToolMarkerCanEdit):
|
||||
:param column: the column
|
||||
:param body: the body/content to be inserted
|
||||
"""
|
||||
self.symbol_manager.insert_before(
|
||||
self.symbol_manager.insert_before_symbol_at_location(
|
||||
SymbolLocation(relative_path, line, column),
|
||||
body=body,
|
||||
)
|
||||
|
||||
+112
-5
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Iterator, Sequence
|
||||
@@ -337,7 +338,7 @@ class SymbolManager:
|
||||
|
||||
def find_by_name(
|
||||
self,
|
||||
name: str,
|
||||
name_path: str,
|
||||
include_body: bool = False,
|
||||
include_kinds: Sequence[SymbolKind] | None = None,
|
||||
exclude_kinds: Sequence[SymbolKind] | None = None,
|
||||
@@ -353,7 +354,9 @@ class SymbolManager:
|
||||
symbol_roots = self.lang_server.request_full_symbol_tree(within_relative_path=within_relative_path, include_body=include_body)
|
||||
for root in symbol_roots:
|
||||
symbols.extend(
|
||||
Symbol(root).find(name, include_kinds=include_kinds, exclude_kinds=exclude_kinds, substring_matching=substring_matching)
|
||||
Symbol(root).find(
|
||||
name_path, include_kinds=include_kinds, exclude_kinds=exclude_kinds, substring_matching=substring_matching
|
||||
)
|
||||
)
|
||||
return symbols
|
||||
|
||||
@@ -373,6 +376,35 @@ class SymbolManager:
|
||||
return None
|
||||
|
||||
def find_referencing_symbols(
|
||||
self,
|
||||
name_path: str,
|
||||
relative_file_path: str,
|
||||
include_body: bool = False,
|
||||
include_kinds: Sequence[SymbolKind] | None = None,
|
||||
exclude_kinds: Sequence[SymbolKind] | None = None,
|
||||
) -> tuple[Symbol, list[Symbol]] | None:
|
||||
"""
|
||||
Find all symbols that reference the symbol with the given name.
|
||||
If multiple symbols fit the name (e.g. for variables that are overwritten), will use the first one.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, substring_matching=False, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
log.warning(f"No symbol with name {name_path} found in file {relative_file_path}")
|
||||
return None
|
||||
if len(symbol_candidates) > 1:
|
||||
log.error(
|
||||
f"Found {len(symbol_candidates)} symbols with name {name_path} in file {relative_file_path}."
|
||||
f"May be an overwritten variable, in which case you can ignore this error. Proceeding with the first one. "
|
||||
f"Found symbols for {name_path=} in {relative_file_path=}: \n"
|
||||
f""
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
referencing_symbols = self.find_referencing_symbols_by_location(
|
||||
symbol.location, include_body=include_body, include_kinds=include_kinds, exclude_kinds=exclude_kinds
|
||||
)
|
||||
return symbol, referencing_symbols
|
||||
|
||||
def find_referencing_symbols_by_location(
|
||||
self,
|
||||
symbol_location: SymbolLocation,
|
||||
include_body: bool = False,
|
||||
@@ -434,7 +466,24 @@ class SymbolManager:
|
||||
with self._edited_file(location.relative_path):
|
||||
yield symbol
|
||||
|
||||
def replace_body(self, location: SymbolLocation, body: str) -> None:
|
||||
def replace_body(self, name_path: str, relative_file_path: str, body: str) -> None:
|
||||
"""
|
||||
Replace the body of the symbol with the given name in the given file.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
raise ValueError(f"No symbol with name {name_path} found in file {relative_file_path}")
|
||||
if len(symbol_candidates) > 1:
|
||||
raise ValueError(
|
||||
f"Found multiple {len(symbol_candidates)} symbols with name {name_path} in file {relative_file_path}. "
|
||||
"Will not replace the body of any of them, but you can use `replace_body_at_location`, the replace lines tool or other editing "
|
||||
"tools to perform your edits. Their locations are: \n "
|
||||
+ json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
self.replace_body_at_location(symbol.location, body)
|
||||
|
||||
def replace_body_at_location(self, location: SymbolLocation, body: str) -> None:
|
||||
"""
|
||||
Replace the body of the symbol at the given location with the given body
|
||||
|
||||
@@ -454,7 +503,23 @@ class SymbolManager:
|
||||
self.lang_server.delete_text_between_positions(location.relative_path, start_pos, end_pos)
|
||||
self.lang_server.insert_text_at_position(location.relative_path, start_pos["line"], start_pos["character"], body)
|
||||
|
||||
def insert_after(self, location: SymbolLocation, body: str) -> None:
|
||||
def insert_after_symbol(self, name_path: str, relative_file_path: str, body: str) -> None:
|
||||
"""
|
||||
Inserts content after the symbol with the given name in the given file.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
raise ValueError(f"No symbol with name {name_path} found in file {relative_file_path}")
|
||||
if len(symbol_candidates) > 1:
|
||||
raise ValueError(
|
||||
f"Found multiple {len(symbol_candidates)} symbols with name {name_path} in file {relative_file_path}. "
|
||||
f"May be an overwritten variable, in which case you can ignore this error. Proceeding with the last one. "
|
||||
f"Found symbols at locations: \n" + json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[-1]
|
||||
self.insert_after_symbol_at_location(symbol.location, body)
|
||||
|
||||
def insert_after_symbol_at_location(self, location: SymbolLocation, body: str) -> None:
|
||||
"""
|
||||
Appends content after the given symbol
|
||||
|
||||
@@ -472,7 +537,23 @@ class SymbolManager:
|
||||
assert location.relative_path is not None
|
||||
self.lang_server.insert_text_at_position(location.relative_path, pos["line"], pos["character"], body)
|
||||
|
||||
def insert_before(self, location: SymbolLocation, body: str) -> None:
|
||||
def insert_before_symbol(self, name_path: str, relative_file_path: str, body: str) -> None:
|
||||
"""
|
||||
Inserts content before the symbol with the given name in the given file.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
raise ValueError(f"No symbol with name {name_path} found in file {relative_file_path}")
|
||||
if len(symbol_candidates) > 1:
|
||||
raise ValueError(
|
||||
f"Found multiple {len(symbol_candidates)} symbols with name {name_path} in file {relative_file_path}. "
|
||||
f"May be an overwritten variable, in which case you can ignore this error. Proceeding with the first one. "
|
||||
f"Found symbols at locations: \n" + json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
self.insert_before_symbol_at_location(symbol.location, body)
|
||||
|
||||
def insert_before_symbol_at_location(self, location: SymbolLocation, body: str) -> None:
|
||||
"""
|
||||
Inserts content before the given symbol
|
||||
|
||||
@@ -512,3 +593,29 @@ class SymbolManager:
|
||||
start_pos = Position(line=start_line, character=0)
|
||||
end_pos = Position(line=end_line + 1, character=0)
|
||||
self.lang_server.delete_text_between_positions(relative_path, start_pos, end_pos)
|
||||
|
||||
def delete_symbol_at_location(self, location: SymbolLocation) -> None:
|
||||
"""
|
||||
Deletes the symbol at the given location.
|
||||
"""
|
||||
with self._edited_symbol_location(location) as symbol:
|
||||
assert location.relative_path is not None
|
||||
assert symbol.body_start_position is not None
|
||||
assert symbol.body_end_position is not None
|
||||
self.lang_server.delete_text_between_positions(location.relative_path, symbol.body_start_position, symbol.body_end_position)
|
||||
|
||||
def delete_symbol(self, name_path: str, relative_file_path: str) -> None:
|
||||
"""
|
||||
Deletes the symbol with the given name in the given file.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
raise ValueError(f"No symbol with name {name_path} found in file {relative_file_path}")
|
||||
if len(symbol_candidates) > 1:
|
||||
raise ValueError(
|
||||
f"Found multiple {len(symbol_candidates)} symbols with name {name_path} in file {relative_file_path}. "
|
||||
"Will not delete any of them, but you can use `delete_symbol_at_location` or a corresponding tool to perform your edits. "
|
||||
"Their locations are: \n " + json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
self.delete_symbol_at_location(symbol.location)
|
||||
|
||||
Reference in New Issue
Block a user