mirror of
https://github.com/tiennm99/serena.git
synced 2026-08-24 02:25:54 +00:00
Add and use use_same_indentation flag to body-replacement
This commit is contained in:
+28
-11
@@ -643,9 +643,15 @@ class SymbolManager:
|
||||
"""Get the content of a file using the language server."""
|
||||
return self.lang_server.language_server.retrieve_full_file_content(relative_path)
|
||||
|
||||
def replace_body(self, name_path: str, relative_file_path: str, body: str) -> None:
|
||||
def replace_body(self, name_path: str, relative_file_path: str, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Replace the body of the symbol with the given name in the given file.
|
||||
Replace the body of the symbol with the given name_path in the given file.
|
||||
|
||||
:param name_path: the name path of the symbol to replace.
|
||||
:param relative_file_path: the relative path of the file in which the symbol is defined.
|
||||
:param body: the new body
|
||||
:param use_same_indentation: whether to use the same indentation as the original body. This means that
|
||||
the user doesn't have to provide the correct indentation, but can just write the body.
|
||||
"""
|
||||
symbol_candidates = self.find_by_name(name_path, within_relative_path=relative_file_path)
|
||||
if len(symbol_candidates) == 0:
|
||||
@@ -658,27 +664,33 @@ class SymbolManager:
|
||||
+ json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
return self.replace_body_at_location(symbol.location, body)
|
||||
return self.replace_body_at_location(symbol.location, body, use_same_indentation=use_same_indentation)
|
||||
|
||||
def replace_body_at_location(self, location: SymbolLocation, body: str) -> None:
|
||||
def replace_body_at_location(self, location: SymbolLocation, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Replace the body of the symbol at the given location with the given body
|
||||
|
||||
:param location: the location of the symbol to replace.
|
||||
:param body: the new body
|
||||
:param use_same_indentation: whether to use the same indentation as the original body. This means that
|
||||
the user doesn't have to provide the correct indentation, but can just write the body.
|
||||
"""
|
||||
# make sure body always ends with at least one newline
|
||||
if not body.endswith("\n"):
|
||||
body += "\n"
|
||||
|
||||
with self._edited_symbol_location(location) as symbol:
|
||||
assert location.relative_path is not None
|
||||
start_pos = symbol.body_start_position
|
||||
end_pos = symbol.body_end_position
|
||||
if start_pos is None or end_pos is None:
|
||||
raise ValueError(f"Symbol at {location} does not have a defined body range.")
|
||||
start_line, start_col = start_pos["line"], start_pos["character"]
|
||||
if use_same_indentation:
|
||||
indent = " " * start_col
|
||||
body = "\n".join(indent + line for line in body.splitlines())
|
||||
|
||||
# make sure body always ends with at least one newline
|
||||
if not body.endswith("\n"):
|
||||
body += "\n"
|
||||
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)
|
||||
self.lang_server.insert_text_at_position(location.relative_path, start_line, start_col, body)
|
||||
|
||||
def insert_after_symbol(
|
||||
self,
|
||||
@@ -731,14 +743,17 @@ class SymbolManager:
|
||||
raise ValueError(f"Symbol at {location} does not have a defined end position.")
|
||||
|
||||
line, col = pos["line"], pos["character"]
|
||||
if at_new_line:
|
||||
line += 1
|
||||
col = 0
|
||||
if not body.startswith("\n"):
|
||||
body = "\n" + body
|
||||
if use_same_indentation:
|
||||
symbol_start_pos = symbol.body_start_position
|
||||
assert symbol_start_pos is not None, f"Symbol at {location=} does not have a defined start position."
|
||||
symbol_identifier_col = symbol_start_pos["character"]
|
||||
indent = " " * (symbol_identifier_col)
|
||||
body = "\n".join(indent + line for line in body.splitlines())
|
||||
if at_new_line:
|
||||
line += 1
|
||||
# IMPORTANT: without this, the insertion does the wrong thing. See implementation of insert_text_at_position in TextUtils,
|
||||
# it is somewhat counterintuitive (never inserts whitespace)
|
||||
# I am not 100% sure whether col=0 is always the best choice here.
|
||||
@@ -807,6 +822,8 @@ class SymbolManager:
|
||||
if at_new_line:
|
||||
col = 0
|
||||
line -= 1
|
||||
if not body.endswith("\n"):
|
||||
body += "\n"
|
||||
assert location.relative_path is not None
|
||||
|
||||
self.lang_server.insert_text_at_position(location.relative_path, line=line, column=col, text_to_be_inserted=body)
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
|
||||
# Module-level variable with type annotation
|
||||
typed_module_var: int = 42
|
||||
|
||||
new_module_var = "Inserted after typed_module_var"
|
||||
|
||||
# Regular class with class and instance variables
|
||||
@@ -199,7 +200,8 @@
|
||||
reassignable_module_var = 10
|
||||
reassignable_module_var = 20 # Reassigned
|
||||
|
||||
new_module_var = "Inserted after typed_module_var"# Module-level variable with type annotation
|
||||
new_module_var = "Inserted after typed_module_var"
|
||||
# Module-level variable with type annotation
|
||||
typed_module_var: int = 42
|
||||
|
||||
|
||||
@@ -365,6 +367,7 @@
|
||||
result = module_var + " used in function"
|
||||
other_result = reassignable_module_var * 2
|
||||
return result, other_result
|
||||
|
||||
def new_inserted_function():
|
||||
print("This is a new function inserted before another.")
|
||||
|
||||
@@ -462,7 +465,8 @@
|
||||
|
||||
|
||||
def new_inserted_function():
|
||||
print("This is a new function inserted before another.")# Function that uses the module variables
|
||||
print("This is a new function inserted before another.")
|
||||
# Function that uses the module variables
|
||||
def use_module_variables():
|
||||
"""Function that uses module-level variables."""
|
||||
result = module_var + " used in function"
|
||||
@@ -495,9 +499,10 @@
|
||||
console.log(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
function newFunctionAfterClass(): void {
|
||||
console.log("This function is after DemoClass.");
|
||||
# }
|
||||
}
|
||||
export function helperFunction() {
|
||||
const demo = new DemoClass(42);
|
||||
demo.printValue();
|
||||
@@ -511,7 +516,8 @@
|
||||
'''
|
||||
function newFunctionAfterClass(): void {
|
||||
console.log("This function is after DemoClass.");
|
||||
# }export class DemoClass {
|
||||
}
|
||||
export class DemoClass {
|
||||
value: number;
|
||||
constructor(value: number) {
|
||||
this.value = value;
|
||||
@@ -546,6 +552,7 @@
|
||||
const demo = new DemoClass(42);
|
||||
demo.printValue();
|
||||
}
|
||||
|
||||
function newInsertedFunction(): void {
|
||||
console.log("This is a new function inserted before another.");
|
||||
}
|
||||
@@ -567,6 +574,7 @@
|
||||
function newInsertedFunction(): void {
|
||||
console.log("This is a new function inserted before another.");
|
||||
}
|
||||
|
||||
export function helperFunction() {
|
||||
const demo = new DemoClass(42);
|
||||
demo.printValue();
|
||||
@@ -621,7 +629,9 @@
|
||||
# Instance variable with type annotation
|
||||
self.typed_instance_var: list[str] = ["item1", "item2"]
|
||||
|
||||
# This body has been replaced
|
||||
|
||||
def modify_instance_var(self):
|
||||
# This body has been replaced
|
||||
self.instance_var = "Replaced!"
|
||||
self.reassignable_instance_var = 999
|
||||
# Reassigned
|
||||
@@ -684,8 +694,11 @@
|
||||
constructor(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
// This body has been replaced
|
||||
|
||||
function printValue() {
|
||||
// This body has been replaced
|
||||
console.warn("New value: " + this.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ NEW_PYTHON_VARIABLE = 'new_module_var = "Inserted after typed_module_var"'
|
||||
|
||||
NEW_TYPESCRIPT_FUNCTION_AFTER = """function newFunctionAfterClass(): void {
|
||||
console.log("This function is after DemoClass.");
|
||||
# }"""
|
||||
}"""
|
||||
|
||||
|
||||
class InsertInRelToSymbolTest(EditingTest):
|
||||
@@ -187,13 +187,18 @@ def test_insert_in_rel_to_symbol(test_case: InsertInRelToSymbolTest, mode: Liter
|
||||
test_case.run_test(content_after_ground_truth=snapshot)
|
||||
|
||||
|
||||
PYTHON_REPLACED_BODY = """ # This body has been replaced
|
||||
self.instance_var = "Replaced!"
|
||||
self.reassignable_instance_var = 999
|
||||
PYTHON_REPLACED_BODY = """
|
||||
def modify_instance_var(self):
|
||||
# This body has been replaced
|
||||
self.instance_var = "Replaced!"
|
||||
self.reassignable_instance_var = 999
|
||||
"""
|
||||
|
||||
TYPESCRIPT_REPLACED_BODY = """ // This body has been replaced
|
||||
console.warn("New value: " + this.value);
|
||||
TYPESCRIPT_REPLACED_BODY = """
|
||||
function printValue() {
|
||||
// This body has been replaced
|
||||
console.warn("New value: " + this.value);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user