mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-01 18:25:00 +00:00
style: Run linter on patch_flex_coder.py
This commit is contained in:
parent
5573cdfba1
commit
4e1e77890b
1 changed files with 66 additions and 61 deletions
|
@ -10,7 +10,6 @@ from ..dump import dump # noqa: F401
|
||||||
from .base_coder import Coder
|
from .base_coder import Coder
|
||||||
from .patch_prompts import PatchPrompts
|
from .patch_prompts import PatchPrompts
|
||||||
|
|
||||||
|
|
||||||
# Import search_replace utilities
|
# Import search_replace utilities
|
||||||
from .search_replace import editblock_strategies, flexible_search_and_replace
|
from .search_replace import editblock_strategies, flexible_search_and_replace
|
||||||
|
|
||||||
|
@ -167,7 +166,7 @@ def _peek_change_hunk(
|
||||||
elif line_type == "delete":
|
elif line_type == "delete":
|
||||||
# This implies interleaved +/- lines which this simplified parser doesn't handle well.
|
# This implies interleaved +/- lines which this simplified parser doesn't handle well.
|
||||||
# Treat as end of hunk? Or raise error? Let's treat as end for now.
|
# Treat as end of hunk? Or raise error? Let's treat as end for now.
|
||||||
index = current_line_index # Put the delete line back for the next hunk
|
index = current_line_index # Put the delete line back for the next hunk
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise DiffError(f"Unexpected line type '{line_type}' in mode '{mode}': {line}")
|
raise DiffError(f"Unexpected line type '{line_type}' in mode '{mode}': {line}")
|
||||||
|
@ -200,17 +199,17 @@ def _peek_change_hunk(
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# PatchFlexCoder Class Implementation
|
# PatchFlexCoder Class Implementation
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
class PatchFlexCoder(Coder): # Rename class
|
class PatchFlexCoder(Coder): # Rename class
|
||||||
"""
|
"""
|
||||||
A coder that uses the patch format for LLM output, but applies changes
|
A coder that uses the patch format for LLM output, but applies changes
|
||||||
using flexible search-and-replace logic for UPDATE actions, ignoring @@ hints
|
using flexible search-and-replace logic for UPDATE actions, ignoring @@ hints
|
||||||
and precise line numbers during application.
|
and precise line numbers during application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
edit_format = "patch-flex" # Give it a distinct name
|
edit_format = "patch-flex" # Give it a distinct name
|
||||||
gpt_prompts = PatchPrompts() # Use the same prompts as PatchCoder
|
gpt_prompts = PatchPrompts() # Use the same prompts as PatchCoder
|
||||||
|
|
||||||
def get_edits(self) -> List[ParsedEdit]: # Return type changed
|
def get_edits(self) -> List[ParsedEdit]: # Return type changed
|
||||||
"""
|
"""
|
||||||
Parses the LLM response content (containing the patch) into a list of
|
Parses the LLM response content (containing the patch) into a list of
|
||||||
ParsedEdit objects, extracting search/replace blocks for UPDATEs.
|
ParsedEdit objects, extracting search/replace blocks for UPDATEs.
|
||||||
|
@ -221,13 +220,10 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
|
|
||||||
lines = content.splitlines()
|
lines = content.splitlines()
|
||||||
start_index = 0
|
start_index = 0
|
||||||
if (
|
if len(lines) >= 2 and _norm(lines[0]).startswith("*** Begin Patch"):
|
||||||
len(lines) >= 2
|
|
||||||
and _norm(lines[0]).startswith("*** Begin Patch")
|
|
||||||
):
|
|
||||||
start_index = 1
|
start_index = 1
|
||||||
else:
|
else:
|
||||||
# Tolerate missing sentinels if content looks like a patch action
|
# Tolerate missing sentinels if content looks like a patch action
|
||||||
is_patch_like = any(
|
is_patch_like = any(
|
||||||
_norm(line).startswith(
|
_norm(line).startswith(
|
||||||
("@@", "*** Update File:", "*** Add File:", "*** Delete File:")
|
("@@", "*** Update File:", "*** Add File:", "*** Delete File:")
|
||||||
|
@ -237,9 +233,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
if not is_patch_like:
|
if not is_patch_like:
|
||||||
self.io.tool_warning("Response does not appear to be in patch format.")
|
self.io.tool_warning("Response does not appear to be in patch format.")
|
||||||
return []
|
return []
|
||||||
self.io.tool_warning(
|
self.io.tool_warning("Patch format warning: Missing '*** Begin Patch' sentinel.")
|
||||||
"Patch format warning: Missing '*** Begin Patch' sentinel."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Identify files needed for context lookups (only for DELETE check)
|
# Identify files needed for context lookups (only for DELETE check)
|
||||||
needed_paths = identify_files_needed(content)
|
needed_paths = identify_files_needed(content)
|
||||||
|
@ -248,7 +242,6 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
# We read content dynamically in apply_edits.
|
# We read content dynamically in apply_edits.
|
||||||
known_files = set(self.get_inchat_relative_files()) | set(needed_paths)
|
known_files = set(self.get_inchat_relative_files()) | set(needed_paths)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Parse the patch text into ParsedEdit objects
|
# Parse the patch text into ParsedEdit objects
|
||||||
parsed_edits = self._parse_patch_text(lines, start_index, known_files)
|
parsed_edits = self._parse_patch_text(lines, start_index, known_files)
|
||||||
|
@ -272,7 +265,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
while index < len(lines):
|
while index < len(lines):
|
||||||
line = lines[index]
|
line = lines[index]
|
||||||
norm_line = _norm(line)
|
norm_line = _norm(line)
|
||||||
line_num = index + 1 # 1-based for reporting
|
line_num = index + 1 # 1-based for reporting
|
||||||
|
|
||||||
if norm_line == "*** End Patch":
|
if norm_line == "*** End Patch":
|
||||||
index += 1
|
index += 1
|
||||||
|
@ -289,7 +282,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
# self.io.tool_warning(f"Update File target '{path}' not found in chat context.")
|
# self.io.tool_warning(f"Update File target '{path}' not found in chat context.")
|
||||||
|
|
||||||
current_file_path = path
|
current_file_path = path
|
||||||
current_move_path = None # Reset move path for new file
|
current_move_path = None # Reset move path for new file
|
||||||
|
|
||||||
# Check for optional Move to immediately after
|
# Check for optional Move to immediately after
|
||||||
if index < len(lines) and _norm(lines[index]).startswith("*** Move to: "):
|
if index < len(lines) and _norm(lines[index]).startswith("*** Move to: "):
|
||||||
|
@ -298,7 +291,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
if not move_to:
|
if not move_to:
|
||||||
raise DiffError(f"Move to action missing path (line {index}).")
|
raise DiffError(f"Move to action missing path (line {index}).")
|
||||||
current_move_path = move_to
|
current_move_path = move_to
|
||||||
continue # Continue to parse hunks for this file
|
continue # Continue to parse hunks for this file
|
||||||
|
|
||||||
# ---------- DELETE ---------- #
|
# ---------- DELETE ---------- #
|
||||||
elif norm_line.startswith("*** Delete File: "):
|
elif norm_line.startswith("*** Delete File: "):
|
||||||
|
@ -307,11 +300,13 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
if not path:
|
if not path:
|
||||||
raise DiffError(f"Delete File action missing path (line {line_num}).")
|
raise DiffError(f"Delete File action missing path (line {line_num}).")
|
||||||
if path not in known_files:
|
if path not in known_files:
|
||||||
# Check against known files before adding delete action
|
# Check against known files before adding delete action
|
||||||
self.io.tool_warning(f"Delete File target '{path}' not found in chat context.")
|
self.io.tool_warning(f"Delete File target '{path}' not found in chat context.")
|
||||||
|
|
||||||
parsed_edits.append(ParsedEdit(path=path, type=ActionType.DELETE, patch_line_num=line_num))
|
parsed_edits.append(
|
||||||
current_file_path = None # Reset current file context
|
ParsedEdit(path=path, type=ActionType.DELETE, patch_line_num=line_num)
|
||||||
|
)
|
||||||
|
current_file_path = None # Reset current file context
|
||||||
current_move_path = None
|
current_move_path = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -328,13 +323,13 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
action.path = path
|
action.path = path
|
||||||
action.patch_line_num = line_num
|
action.patch_line_num = line_num
|
||||||
parsed_edits.append(action)
|
parsed_edits.append(action)
|
||||||
current_file_path = None # Reset current file context
|
current_file_path = None # Reset current file context
|
||||||
current_move_path = None
|
current_move_path = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ---------- Hunks within UPDATE ---------- #
|
# ---------- Hunks within UPDATE ---------- #
|
||||||
elif current_file_path:
|
elif current_file_path:
|
||||||
# Skip @@ lines, they are ignored by this coder's application logic
|
# Skip @@ lines, they are ignored by this coder's application logic
|
||||||
if norm_line.startswith("@@"):
|
if norm_line.startswith("@@"):
|
||||||
index += 1
|
index += 1
|
||||||
continue
|
continue
|
||||||
|
@ -348,12 +343,11 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
ins_lines,
|
ins_lines,
|
||||||
context_after,
|
context_after,
|
||||||
next_index,
|
next_index,
|
||||||
_is_eof, # EOF marker not strictly needed for search/replace logic
|
_is_eof, # EOF marker not strictly needed for search/replace logic
|
||||||
) = _peek_change_hunk(lines, index)
|
) = _peek_change_hunk(lines, index)
|
||||||
except DiffError as e:
|
except DiffError as e:
|
||||||
raise DiffError(f"{e} (near line {line_num} in patch)")
|
raise DiffError(f"{e} (near line {line_num} in patch)")
|
||||||
|
|
||||||
|
|
||||||
if not del_lines and not ins_lines:
|
if not del_lines and not ins_lines:
|
||||||
# Skip hunks that contain only context - they don't represent a change
|
# Skip hunks that contain only context - they don't represent a change
|
||||||
index = next_index
|
index = next_index
|
||||||
|
@ -371,15 +365,14 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
# the original block likely ended with the last deleted line.
|
# the original block likely ended with the last deleted line.
|
||||||
# Or if context_before/del/ins are all empty, it's just context.
|
# Or if context_before/del/ins are all empty, it's just context.
|
||||||
if not context_after and (del_lines or ins_lines):
|
if not context_after and (del_lines or ins_lines):
|
||||||
search_text += "\n"
|
search_text += "\n"
|
||||||
# Replace text already includes context_after, so only add if that was empty too
|
# Replace text already includes context_after, so only add if that was empty too
|
||||||
if not ins_lines:
|
if not ins_lines:
|
||||||
replace_text += "\n"
|
replace_text += "\n"
|
||||||
elif context_after or context_before or del_lines or ins_lines:
|
elif context_after or context_before or del_lines or ins_lines:
|
||||||
# If there's any content, ensure trailing newline for consistency
|
# If there's any content, ensure trailing newline for consistency
|
||||||
search_text += "\n"
|
search_text += "\n"
|
||||||
replace_text += "\n"
|
replace_text += "\n"
|
||||||
|
|
||||||
|
|
||||||
parsed_edits.append(
|
parsed_edits.append(
|
||||||
ParsedEdit(
|
ParsedEdit(
|
||||||
|
@ -387,7 +380,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
type=ActionType.UPDATE,
|
type=ActionType.UPDATE,
|
||||||
search_text=search_text,
|
search_text=search_text,
|
||||||
replace_text=replace_text,
|
replace_text=replace_text,
|
||||||
move_path=current_move_path, # Carry over move path for this hunk
|
move_path=current_move_path, # Carry over move path for this hunk
|
||||||
patch_line_num=hunk_start_index + 1,
|
patch_line_num=hunk_start_index + 1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -395,11 +388,13 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If we are here, the line is unexpected or misplaced
|
# If we are here, the line is unexpected or misplaced
|
||||||
if not norm_line.strip(): # Allow blank lines between actions/files
|
if not norm_line.strip(): # Allow blank lines between actions/files
|
||||||
index += 1
|
index += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise DiffError(f"Unknown or misplaced line while parsing patch (line {line_num}): {line}")
|
raise DiffError(
|
||||||
|
f"Unknown or misplaced line while parsing patch (line {line_num}): {line}"
|
||||||
|
)
|
||||||
|
|
||||||
return parsed_edits
|
return parsed_edits
|
||||||
|
|
||||||
|
@ -423,7 +418,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
|
|
||||||
if not line.startswith("+"):
|
if not line.startswith("+"):
|
||||||
if norm_line.strip() == "":
|
if norm_line.strip() == "":
|
||||||
added_lines.append("") # Treat blank line as adding a blank line
|
added_lines.append("") # Treat blank line as adding a blank line
|
||||||
else:
|
else:
|
||||||
raise DiffError(f"Invalid Add File line (missing '+') (line {index+1}): {line}")
|
raise DiffError(f"Invalid Add File line (missing '+') (line {index+1}): {line}")
|
||||||
else:
|
else:
|
||||||
|
@ -432,14 +427,14 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
action = ParsedEdit(
|
action = ParsedEdit(
|
||||||
path="", # Path set by caller
|
path="", # Path set by caller
|
||||||
type=ActionType.ADD,
|
type=ActionType.ADD,
|
||||||
new_content="\n".join(added_lines),
|
new_content="\n".join(added_lines),
|
||||||
patch_line_num=start_line_num
|
patch_line_num=start_line_num,
|
||||||
)
|
)
|
||||||
return action, index
|
return action, index
|
||||||
|
|
||||||
def apply_edits(self, edits: List[ParsedEdit]): # Argument type changed
|
def apply_edits(self, edits: List[ParsedEdit]): # Argument type changed
|
||||||
"""
|
"""
|
||||||
Applies the parsed edits. Uses flexible search-and-replace for UPDATEs.
|
Applies the parsed edits. Uses flexible search-and-replace for UPDATEs.
|
||||||
"""
|
"""
|
||||||
|
@ -456,7 +451,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
path_obj = pathlib.Path(full_path)
|
path_obj = pathlib.Path(full_path)
|
||||||
current_content = None
|
current_content = None
|
||||||
edit_failed = False
|
edit_failed = False
|
||||||
final_move_path = None # Track the last move destination for this file
|
final_move_path = None # Track the last move destination for this file
|
||||||
|
|
||||||
# Check for simple ADD/DELETE first (should ideally be only one per file)
|
# Check for simple ADD/DELETE first (should ideally be only one per file)
|
||||||
if len(path_edits) == 1 and path_edits[0].type in [ActionType.ADD, ActionType.DELETE]:
|
if len(path_edits) == 1 and path_edits[0].type in [ActionType.ADD, ActionType.DELETE]:
|
||||||
|
@ -464,8 +459,10 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
try:
|
try:
|
||||||
if edit.type == ActionType.ADD:
|
if edit.type == ActionType.ADD:
|
||||||
if path_obj.exists():
|
if path_obj.exists():
|
||||||
# Allow overwrite on ADD? Or error? Let's warn and overwrite.
|
# Allow overwrite on ADD? Or error? Let's warn and overwrite.
|
||||||
self.io.tool_warning(f"ADD Warning: File '{path}' already exists, overwriting.")
|
self.io.tool_warning(
|
||||||
|
f"ADD Warning: File '{path}' already exists, overwriting."
|
||||||
|
)
|
||||||
# raise DiffError(f"ADD Error: File already exists: {path}")
|
# raise DiffError(f"ADD Error: File already exists: {path}")
|
||||||
if edit.new_content is None:
|
if edit.new_content is None:
|
||||||
raise DiffError(f"ADD change for {path} has no content")
|
raise DiffError(f"ADD change for {path} has no content")
|
||||||
|
@ -480,14 +477,18 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
elif edit.type == ActionType.DELETE:
|
elif edit.type == ActionType.DELETE:
|
||||||
self.io.tool_output(f"Deleting {path}")
|
self.io.tool_output(f"Deleting {path}")
|
||||||
if not path_obj.exists():
|
if not path_obj.exists():
|
||||||
self.io.tool_warning(f"DELETE Warning: File not found, skipping: {path}")
|
self.io.tool_warning(
|
||||||
|
f"DELETE Warning: File not found, skipping: {path}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
path_obj.unlink()
|
path_obj.unlink()
|
||||||
except (DiffError, FileNotFoundError, IOError, OSError) as e:
|
except (DiffError, FileNotFoundError, IOError, OSError) as e:
|
||||||
raise ValueError(f"Error applying action '{edit.type}' to {path}: {e}")
|
raise ValueError(f"Error applying action '{edit.type}' to {path}: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Unexpected error applying action '{edit.type}' to {path}: {e}")
|
raise ValueError(
|
||||||
continue # Move to the next file path
|
f"Unexpected error applying action '{edit.type}' to {path}: {e}"
|
||||||
|
)
|
||||||
|
continue # Move to the next file path
|
||||||
|
|
||||||
# --- Handle UPDATE actions sequentially ---
|
# --- Handle UPDATE actions sequentially ---
|
||||||
self.io.tool_output(f"Updating {path}...")
|
self.io.tool_output(f"Updating {path}...")
|
||||||
|
@ -500,13 +501,17 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
|
|
||||||
for i, edit in enumerate(path_edits):
|
for i, edit in enumerate(path_edits):
|
||||||
if edit.type != ActionType.UPDATE:
|
if edit.type != ActionType.UPDATE:
|
||||||
raise DiffError(f"Unexpected action type '{edit.type}' mixed with UPDATE for {path}")
|
raise DiffError(
|
||||||
|
f"Unexpected action type '{edit.type}' mixed with UPDATE for {path}"
|
||||||
|
)
|
||||||
if edit.search_text is None or edit.replace_text is None:
|
if edit.search_text is None or edit.replace_text is None:
|
||||||
raise DiffError(f"UPDATE action for {path} is missing search/replace text")
|
raise DiffError(f"UPDATE action for {path} is missing search/replace text")
|
||||||
|
|
||||||
final_move_path = edit.move_path # Last move path specified wins
|
final_move_path = edit.move_path # Last move path specified wins
|
||||||
|
|
||||||
self.io.tool_output(f" Applying hunk {i+1} (from patch line {edit.patch_line_num})...")
|
self.io.tool_output(
|
||||||
|
f" Applying hunk {i+1} (from patch line {edit.patch_line_num})..."
|
||||||
|
)
|
||||||
|
|
||||||
texts = (edit.search_text, edit.replace_text, current_content)
|
texts = (edit.search_text, edit.replace_text, current_content)
|
||||||
new_content = flexible_search_and_replace(texts, editblock_strategies)
|
new_content = flexible_search_and_replace(texts, editblock_strategies)
|
||||||
|
@ -515,11 +520,11 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
edit_failed = True
|
edit_failed = True
|
||||||
# Provide more context on failure
|
# Provide more context on failure
|
||||||
err_msg = (
|
err_msg = (
|
||||||
f"Failed to apply update hunk {i+1} (from patch line {edit.patch_line_num})"
|
f"Failed to apply update hunk {i+1} (from patch line"
|
||||||
f" for file {path}. The search block may not have been found"
|
f" {edit.patch_line_num}) for file {path}. The search block may not"
|
||||||
" or the change conflicted.\n"
|
" have been found or the change conflicted.\nSearch"
|
||||||
f"Search block:\n```\n{edit.search_text}```\n"
|
f" block:\n```\n{edit.search_text}```\nReplace"
|
||||||
f"Replace block:\n```\n{edit.replace_text}```"
|
f" block:\n```\n{edit.replace_text}```"
|
||||||
)
|
)
|
||||||
# Raise immediately to stop processing this file
|
# Raise immediately to stop processing this file
|
||||||
raise ValueError(err_msg)
|
raise ValueError(err_msg)
|
||||||
|
@ -546,7 +551,7 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
target_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
target_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
||||||
# Ensure trailing newline
|
# Ensure trailing newline
|
||||||
if not current_content.endswith("\n") and current_content != "":
|
if not current_content.endswith("\n") and current_content != "":
|
||||||
current_content += "\n"
|
current_content += "\n"
|
||||||
self.io.write_text(target_full_path, current_content)
|
self.io.write_text(target_full_path, current_content)
|
||||||
|
|
||||||
# Remove original file *after* successful write if moved
|
# Remove original file *after* successful write if moved
|
||||||
|
@ -554,11 +559,11 @@ class PatchFlexCoder(Coder): # Rename class
|
||||||
path_obj.unlink()
|
path_obj.unlink()
|
||||||
|
|
||||||
except (DiffError, FileNotFoundError, IOError, OSError) as e:
|
except (DiffError, FileNotFoundError, IOError, OSError) as e:
|
||||||
# Raise a ValueError to signal failure
|
# Raise a ValueError to signal failure
|
||||||
raise ValueError(f"Error applying UPDATE to {path}: {e}")
|
raise ValueError(f"Error applying UPDATE to {path}: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Catch unexpected errors during application
|
# Catch unexpected errors during application
|
||||||
raise ValueError(f"Unexpected error applying UPDATE to {path}: {e}")
|
raise ValueError(f"Unexpected error applying UPDATE to {path}: {e}")
|
||||||
|
|
||||||
# Remove the _apply_update method as it's replaced by flexible_search_and_replace logic
|
# Remove the _apply_update method as it's replaced by flexible_search_and_replace logic
|
||||||
# def _apply_update(self, text: str, action: PatchAction, path: str) -> str:
|
# def _apply_update(self, text: str, action: PatchAction, path: str) -> str:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue