Add more real Git-style diffs in command feedback

This commit is contained in:
Amar Sood (tekacs) 2025-04-12 10:44:53 -04:00
parent b717384420
commit a4bcf4f938
9 changed files with 63 additions and 17 deletions

View file

@ -8,6 +8,7 @@ from .tool_utils import (
apply_change,
handle_tool_error,
format_tool_result,
generate_unified_diff_snippet,
)
def _execute_delete_block(coder, file_path, start_pattern, end_pattern=None, line_count=None, near_context=None, occurrence=1, change_id=None, dry_run=False):
@ -50,7 +51,7 @@ def _execute_delete_block(coder, file_path, start_pattern, end_pattern=None, lin
return f"Warning: No changes made (deletion would not change file)"
# 5. Generate diff for feedback
diff_snippet = coder._generate_diff_snippet_delete(original_content, start_line, end_line)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
num_deleted = end_line - start_line + 1
num_occurrences = len(start_pattern_indices)
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
@ -86,4 +87,4 @@ def _execute_delete_block(coder, file_path, start_pattern, end_pattern=None, lin
return handle_tool_error(coder, tool_name, e, add_traceback=False)
except Exception as e:
# Handle unexpected errors
return handle_tool_error(coder, tool_name, e)
return handle_tool_error(coder, tool_name, e)

View file

@ -1,5 +1,6 @@
import os
import traceback
from .tool_utils import generate_unified_diff_snippet
def _execute_delete_line(coder, file_path, line_number, change_id=None, dry_run=False):
"""
@ -62,7 +63,7 @@ def _execute_delete_line(coder, file_path, line_number, change_id=None, dry_run=
return f"Warning: No changes made (deleting line {line_num_int} would not change file)"
# Generate diff snippet (using the existing delete block helper for simplicity)
diff_snippet = coder._generate_diff_snippet_delete(original_content, line_idx, line_idx)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
# Handle dry run
if dry_run:
@ -97,4 +98,4 @@ def _execute_delete_line(coder, file_path, line_number, change_id=None, dry_run=
except Exception as e:
coder.io.tool_error(f"Error in DeleteLine: {str(e)}\n{traceback.format_exc()}")
return f"Error: {str(e)}"
return f"Error: {str(e)}"

View file

@ -1,5 +1,6 @@
import os
import traceback
from .tool_utils import generate_unified_diff_snippet
def _execute_delete_lines(coder, file_path, start_line, end_line, change_id=None, dry_run=False):
"""
@ -71,7 +72,7 @@ def _execute_delete_lines(coder, file_path, start_line, end_line, change_id=None
return f"Warning: No changes made (deleting lines {start_line_int}-{end_line_int} would not change file)"
# Generate diff snippet
diff_snippet = coder._generate_diff_snippet_delete(original_content, start_idx, end_idx)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
# Handle dry run
if dry_run:
@ -108,4 +109,4 @@ def _execute_delete_lines(coder, file_path, start_line, end_line, change_id=None
except Exception as e:
coder.io.tool_error(f"Error in DeleteLines: {str(e)}\n{traceback.format_exc()}")
return f"Error: {str(e)}"
return f"Error: {str(e)}"

View file

@ -1,5 +1,6 @@
import os
import traceback
from .tool_utils import generate_unified_diff_snippet
def _execute_extract_lines(coder, source_file_path, target_file_path, start_pattern, end_pattern=None, line_count=None, near_context=None, occurrence=1, dry_run=False):
"""
@ -145,9 +146,9 @@ def _execute_extract_lines(coder, source_file_path, target_file_path, start_patt
new_target_content = target_content + extracted_block
# --- Generate Diffs ---
source_diff_snippet = coder._generate_diff_snippet_delete(original_source_content, start_line, end_line)
source_diff_snippet = generate_unified_diff_snippet(original_source_content, new_source_content, rel_source_path)
target_insertion_line = len(target_content.splitlines()) if target_content else 0
target_diff_snippet = coder._generate_diff_snippet_insert(original_target_content, target_insertion_line, extracted_lines)
target_diff_snippet = generate_unified_diff_snippet(original_target_content, new_target_content, rel_target_path)
# --- Handle Dry Run ---
if dry_run:
@ -217,4 +218,4 @@ def _execute_extract_lines(coder, source_file_path, target_file_path, start_patt
except Exception as e:
coder.io.tool_error(f"Error in ExtractLines: {str(e)}\n{traceback.format_exc()}")
return f"Error: {str(e)}"
return f"Error: {str(e)}"

View file

@ -9,6 +9,7 @@ from .tool_utils import (
apply_change,
handle_tool_error,
format_tool_result,
generate_unified_diff_snippet,
)
def _execute_indent_lines(coder, file_path, start_pattern, end_pattern=None, line_count=None, indent_levels=1, near_context=None, occurrence=1, change_id=None, dry_run=False):
@ -81,7 +82,7 @@ def _execute_indent_lines(coder, file_path, start_pattern, end_pattern=None, lin
return f"Warning: No changes made (indentation would not change file)"
# 5. Generate diff for feedback
diff_snippet = coder._generate_diff_snippet_indent(original_content, new_content, start_line, end_line)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
num_occurrences = len(start_pattern_indices)
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
action = "indent" if indent_levels > 0 else "unindent"

View file

@ -8,6 +8,7 @@ from .tool_utils import (
apply_change,
handle_tool_error,
format_tool_result,
generate_unified_diff_snippet,
)
def _execute_insert_block(coder, file_path, content, after_pattern=None, before_pattern=None, near_context=None, occurrence=1, change_id=None, dry_run=False):
@ -51,7 +52,7 @@ def _execute_insert_block(coder, file_path, content, after_pattern=None, before_
return f"Warning: No changes made (insertion would not change file)"
# 5. Generate diff for feedback
diff_snippet = coder._generate_diff_snippet_insert(original_content, insertion_line_idx, content_lines)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
num_occurrences = len(pattern_line_indices)
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""

View file

@ -1,5 +1,7 @@
import os
import traceback
from .tool_utils import generate_unified_diff_snippet
from .tool_utils import generate_unified_diff_snippet
def _execute_replace_lines(coder, file_path, start_line, end_line, new_content, change_id=None, dry_run=False):
"""
@ -7,7 +9,6 @@ def _execute_replace_lines(coder, file_path, start_line, end_line, new_content,
Useful for fixing errors identified by error messages or linters.
Parameters:
- coder: The Coder instance
- file_path: Path to the file to modify
- start_line: The first line number to replace (1-based)
- end_line: The last line number to replace (1-based)
@ -86,6 +87,7 @@ def _execute_replace_lines(coder, file_path, start_line, end_line, new_content,
if original_content == new_content_full:
coder.io.tool_warning("No changes made: new content is identical to original")
return f"Warning: No changes made (new content identical to original)"
diff_snippet = generate_unified_diff_snippet(original_content, new_content_full, rel_path)
# Create a readable diff for the lines replacement
diff = f"Lines {start_line}-{end_line}:\n"
@ -101,7 +103,7 @@ def _execute_replace_lines(coder, file_path, start_line, end_line, new_content,
# Handle dry run
if dry_run:
coder.io.tool_output(f"Dry run: Would replace lines {start_line}-{end_line} in {file_path}")
return f"Dry run: Would replace lines {start_line}-{end_line}. Diff:\n{diff}"
return f"Dry run: Would replace lines {start_line}-{end_line}. Diff snippet:\n{diff_snippet}"
# --- Apply Change (Not dry run) ---
coder.io.write_text(abs_path, new_content_full)
@ -136,4 +138,4 @@ def _execute_replace_lines(coder, file_path, start_line, end_line, new_content,
except Exception as e:
coder.io.tool_error(f"Error in ReplaceLines: {str(e)}\n{traceback.format_exc()}")
return f"Error: {str(e)}"
return f"Error: {str(e)}"

View file

@ -5,6 +5,7 @@ from .tool_utils import (
apply_change,
handle_tool_error,
format_tool_result,
generate_unified_diff_snippet,
)
def _execute_replace_text(coder, file_path, find_text, replace_text, near_context=None, occurrence=1, change_id=None, dry_run=False):
@ -57,7 +58,7 @@ def _execute_replace_text(coder, file_path, find_text, replace_text, near_contex
# 5. Generate diff for feedback
# Note: _generate_diff_snippet is currently on the Coder class
diff_snippet = coder._generate_diff_snippet(original_content, start_index, len(find_text), replace_text)
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
occurrence_str = f"occurrence {occurrence}" if num_occurrences > 1 else "text"
# 6. Handle dry run
@ -88,4 +89,4 @@ def _execute_replace_text(coder, file_path, find_text, replace_text, near_contex
return handle_tool_error(coder, tool_name, e, add_traceback=False)
except Exception as e:
# Handle unexpected errors
return handle_tool_error(coder, tool_name, e)
return handle_tool_error(coder, tool_name, e)

View file

@ -1,3 +1,4 @@
import difflib
import os
import traceback
@ -170,6 +171,42 @@ def determine_line_range(
return start_line, end_line
def generate_unified_diff_snippet(original_content, new_content, file_path, context_lines=3):
"""
Generates a unified diff snippet between original and new content.
Args:
original_content (str): The original file content.
new_content (str): The modified file content.
file_path (str): The relative path to the file (for display in diff header).
context_lines (int): Number of context lines to show around changes.
Returns:
str: A formatted unified diff snippet, or an empty string if no changes.
"""
if original_content == new_content:
return ""
original_lines = original_content.splitlines(keepends=True)
new_lines = new_content.splitlines(keepends=True)
diff = difflib.unified_diff(
original_lines,
new_lines,
fromfile=f"a/{file_path}",
tofile=f"b/{file_path}",
n=context_lines, # Number of context lines
)
# Join the diff lines, potentially skipping the header if desired,
# but let's keep it for standard format.
diff_snippet = "".join(diff)
# Ensure snippet ends with a newline for cleaner formatting in results
if diff_snippet and not diff_snippet.endswith('\n'):
diff_snippet += '\n'
return diff_snippet
def apply_change(coder, abs_path, rel_path, original_content, new_content, change_type, metadata, change_id=None):
"""
Writes the new content, tracks the change, and updates coder state.
@ -237,4 +274,4 @@ def format_tool_result(coder, tool_name, success_message, change_id=None, diff_s
# except ToolError as e:
# return handle_tool_error(coder, "MyTool", e, add_traceback=False) # Don't need traceback for ToolErrors
# except Exception as e:
# return handle_tool_error(coder, "MyTool", e)
# return handle_tool_error(coder, "MyTool", e)