mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-09 06:05:00 +00:00
Add the ability to view numbered context and guidance around line-editing.
This commit is contained in:
parent
9cb0f5e203
commit
9e7ab846e5
3 changed files with 133 additions and 2 deletions
|
@ -54,6 +54,7 @@ from aider.tools.indent_lines import _execute_indent_lines
|
|||
from aider.tools.undo_change import _execute_undo_change
|
||||
from aider.tools.list_changes import _execute_list_changes
|
||||
from aider.tools.extract_lines import _execute_extract_lines
|
||||
from aider.tools.view_numbered_context import execute_view_numbered_context
|
||||
|
||||
class NavigatorCoder(Coder):
|
||||
"""Mode where the LLM autonomously manages which files are in context."""
|
||||
|
@ -876,6 +877,19 @@ class NavigatorCoder(Coder):
|
|||
else:
|
||||
result_message = "Error: Missing required parameters for ExtractLines (source_file_path, target_file_path, start_pattern)"
|
||||
|
||||
elif norm_tool_name == 'viewnumberedcontext':
|
||||
file_path = params.get('file_path')
|
||||
pattern = params.get('pattern')
|
||||
line_number = params.get('line_number')
|
||||
context_lines = params.get('context_lines', 3) # Default context
|
||||
|
||||
if file_path is not None and (pattern is not None or line_number is not None):
|
||||
result_message = execute_view_numbered_context(
|
||||
self, file_path, pattern, line_number, context_lines
|
||||
)
|
||||
else:
|
||||
result_message = "Error: Missing required parameters for ViewNumberedContext (file_path and either pattern or line_number)"
|
||||
|
||||
else:
|
||||
result_message = f"Error: Unknown tool name '{tool_name}'"
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ Act as an expert software engineer with the ability to autonomously navigate and
|
|||
Extract lines from `start_pattern` to `end_pattern` (or use `line_count`) in `source_file_path` and move them to `target_file_path`. Creates `target_file_path` if it doesn't exist. Use `near_context` and `occurrence` (optional, default 1, -1 for last) for `start_pattern`. `dry_run=True` simulates.
|
||||
*Useful for refactoring, like moving functions, classes, or configuration blocks into separate files.*
|
||||
|
||||
- **ViewNumberedContext**: `[tool_call(ViewNumberedContext, file_path="path/to/file.py", pattern="optional_text", line_number=optional_int, context_lines=3)]`
|
||||
Displays numbered lines from `file_path` centered around a target location, without adding the file to context. Provide *either* `pattern` (to find the first occurrence) *or* `line_number` (1-based) to specify the center point. Returns the target line(s) plus `context_lines` (default 3) of surrounding context directly in the result message. Crucial for verifying exact line numbers and content before using `ReplaceLine` or `ReplaceLines`.
|
||||
|
||||
### Other Tools
|
||||
- **Command**: `[tool_call(Command, command_string="git diff HEAD~1")]`
|
||||
Execute a *non-interactive* shell command. Requires user confirmation. Use for commands that don't need user input (e.g., `ls`, `git status`, `cat file`).
|
||||
|
@ -158,6 +161,29 @@ SEARCH/REPLACE blocks can appear anywhere in your response if needed.
|
|||
* Use `ListChanges` to see a history of applied changes.
|
||||
* If you review a result diff (from a direct edit) and find the change was incorrect or applied in the wrong place, use `[tool_call(UndoChange, change_id="...")]` in your *next* message, using the `change_id` provided in the result message. Then, attempt the corrected edit.
|
||||
|
||||
**Using Line Number Based Tools (`ReplaceLine`, `ReplaceLines`):**
|
||||
* **High Risk:** Line numbers are fragile and can become outdated due to preceding edits, even within the same multi-tool message. Using these tools without recent verification can lead to incorrect changes.
|
||||
* **Mandatory Verification Workflow:**
|
||||
1. **Identify Target Location:** Determine the approximate location using line numbers (e.g., from linter output) or nearby text.
|
||||
2. **View Numbered Context (Separate Turn):** In one message, use `ViewNumberedContext` specifying *either* the `line_number` or a nearby `pattern` to display numbered lines for the target area.
|
||||
```
|
||||
# Example using line number
|
||||
---
|
||||
[tool_call(ViewNumberedContext, file_path="path/to/file.py", line_number=APPROX_LINE, context_lines=5)]
|
||||
```
|
||||
```
|
||||
# Example using pattern
|
||||
---
|
||||
[tool_call(ViewNumberedContext, file_path="path/to/file.py", pattern="text_near_target", context_lines=5)]
|
||||
```
|
||||
3. **Verify:** Carefully examine the numbered output in the result message to confirm the *exact* line numbers and content you intend to modify.
|
||||
4. **Edit (Next Turn):** Only in the *next* message, issue the `ReplaceLine` or `ReplaceLines` command using the verified line numbers.
|
||||
```
|
||||
---
|
||||
[tool_call(ReplaceLine, file_path="path/to/file.py", line_number=VERIFIED_LINE, new_content="...")]
|
||||
```
|
||||
* **Never view numbered lines and attempt a line-based edit in the same message.**
|
||||
|
||||
### Context Management Strategy
|
||||
- Keep your context focused by removing files that are no longer relevant
|
||||
- For large codebases, maintain only 5-15 files in context at once for best performance
|
||||
|
@ -177,13 +203,13 @@ For precise, targeted edits to code, use the granular editing tools:
|
|||
- **ReplaceAll**: Replace all occurrences of text in a file (e.g., rename variables)
|
||||
- **InsertBlock**: Insert multi-line blocks of code at specific locations
|
||||
- **DeleteBlock**: Remove specific sections of code
|
||||
- **ReplaceLine/ReplaceLines**: Fix specific line numbers from error messages or linters
|
||||
- **ReplaceLine/ReplaceLines**: Fix specific line numbers from error messages or linters (use with caution, see workflow below)
|
||||
- **IndentLines**: Adjust indentation of code blocks
|
||||
- **UndoChange**: Reverse specific changes by ID if you make a mistake
|
||||
|
||||
#### When to Use Line Number Based Tools
|
||||
|
||||
When dealing with errors or warnings that include line numbers, prefer the line-based editing tools:
|
||||
When dealing with errors or warnings that include line numbers, you *can* use the line-based editing tools, but **you MUST follow the mandatory verification workflow described in the `## Granular Editing Workflow` section above.** This involves using `ViewNumberedContext` in one turn to verify the lines, and then using `ReplaceLine`/`ReplaceLines` in the *next* turn.
|
||||
|
||||
```
|
||||
Error in /path/to/file.py line 42: Syntax error: unexpected token
|
||||
|
|
91
aider/tools/view_numbered_context.py
Normal file
91
aider/tools/view_numbered_context.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
import os
|
||||
|
||||
def execute_view_numbered_context(coder, file_path, pattern=None, line_number=None, context_lines=3):
|
||||
"""
|
||||
Displays numbered lines from file_path centered around a target location
|
||||
(pattern or line_number), without adding the file to context.
|
||||
"""
|
||||
error_message = None
|
||||
if not (pattern is None) ^ (line_number is None):
|
||||
error_message = "Provide exactly one of 'pattern' or 'line_number'."
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
abs_path = coder.abs_root_path(file_path)
|
||||
if not os.path.exists(abs_path):
|
||||
error_message = f"File not found: {file_path}"
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
try:
|
||||
content = coder.io.read_text(abs_path)
|
||||
if content is None:
|
||||
error_message = f"Could not read file: {file_path}"
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
lines = content.splitlines()
|
||||
num_lines = len(lines)
|
||||
|
||||
center_line_idx = -1
|
||||
found_by = ""
|
||||
|
||||
if line_number is not None:
|
||||
try:
|
||||
line_number_int = int(line_number)
|
||||
if 1 <= line_number_int <= num_lines:
|
||||
center_line_idx = line_number_int - 1 # Convert to 0-based index
|
||||
found_by = f"line {line_number_int}"
|
||||
else:
|
||||
error_message = f"Line number {line_number_int} is out of range (1-{num_lines}) for {file_path}."
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
except ValueError:
|
||||
error_message = f"Invalid line number '{line_number}'. Must be an integer."
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
elif pattern is not None:
|
||||
first_match_line_idx = -1
|
||||
for i, line in enumerate(lines):
|
||||
if pattern in line:
|
||||
first_match_line_idx = i
|
||||
break
|
||||
|
||||
if first_match_line_idx != -1:
|
||||
center_line_idx = first_match_line_idx
|
||||
found_by = f"pattern '{pattern}' on line {center_line_idx + 1}"
|
||||
else:
|
||||
error_message = f"Pattern '{pattern}' not found in {file_path}."
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
if center_line_idx == -1:
|
||||
# Should not happen if logic above is correct, but as a safeguard
|
||||
error_message = "Could not determine center line."
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
# Calculate context window
|
||||
try:
|
||||
context_lines_int = int(context_lines)
|
||||
except ValueError:
|
||||
coder.io.tool_warning(f"Invalid context_lines value '{context_lines}', using default 3.")
|
||||
context_lines_int = 3
|
||||
|
||||
start_line_idx = max(0, center_line_idx - context_lines_int)
|
||||
end_line_idx = min(num_lines - 1, center_line_idx + context_lines_int)
|
||||
|
||||
# Format output
|
||||
output_lines = [f"Displaying context around {found_by} in {file_path}:"]
|
||||
max_line_num_width = len(str(end_line_idx + 1)) # Width for padding
|
||||
|
||||
for i in range(start_line_idx, end_line_idx + 1):
|
||||
line_num_str = str(i + 1).rjust(max_line_num_width)
|
||||
output_lines.append(f"{line_num_str} | {lines[i]}")
|
||||
|
||||
return "\n".join(output_lines)
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Error processing {file_path}: {e}"
|
||||
coder.io.tool_error(error_message)
|
||||
return f"Error: {error_message}"
|
Loading…
Add table
Add a link
Reference in a new issue