Files
roadpad/edit_manager.py

152 lines
4.9 KiB
Python

"""
Edit Manager - Track and apply pending edits for RoadPad accept modes.
Handles three modes:
- manual: Show diff, user accepts/rejects
- on save: Queue edits, apply on save
- always: Apply immediately
"""
from typing import List, Tuple, Optional
from datetime import datetime
class Edit:
"""Represents a single pending edit."""
def __init__(self, query: str, response: str, timestamp: str = None):
self.query = query
self.response = response
self.timestamp = timestamp or datetime.now().isoformat()
self.applied = False
self.rejected = False
def __repr__(self):
status = "applied" if self.applied else ("rejected" if self.rejected else "pending")
return f"Edit({self.query[:30]}..., {status})"
class EditManager:
"""Manage pending edits and apply them based on accept mode."""
def __init__(self):
self.pending_edits: List[Edit] = []
self.applied_edits: List[Edit] = []
self.rejected_edits: List[Edit] = []
def add_edit(self, query: str, response: str) -> Edit:
"""Add a new edit to the pending queue."""
edit = Edit(query, response)
self.pending_edits.append(edit)
return edit
def accept_edit(self, edit: Edit) -> bool:
"""Accept and apply a specific edit."""
if edit in self.pending_edits:
edit.applied = True
self.pending_edits.remove(edit)
self.applied_edits.append(edit)
return True
return False
def reject_edit(self, edit: Edit) -> bool:
"""Reject a specific edit."""
if edit in self.pending_edits:
edit.rejected = True
self.pending_edits.remove(edit)
self.rejected_edits.append(edit)
return True
return False
def accept_all(self) -> int:
"""Accept all pending edits. Returns count applied."""
count = len(self.pending_edits)
for edit in list(self.pending_edits):
self.accept_edit(edit)
return count
def reject_all(self) -> int:
"""Reject all pending edits. Returns count rejected."""
count = len(self.pending_edits)
for edit in list(self.pending_edits):
self.reject_edit(edit)
return count
def clear_pending(self) -> None:
"""Clear all pending edits without applying."""
self.pending_edits.clear()
def get_pending_count(self) -> int:
"""Get count of pending edits."""
return len(self.pending_edits)
def get_next_edit(self) -> Optional[Edit]:
"""Get the next pending edit for review."""
return self.pending_edits[0] if self.pending_edits else None
def get_diff_preview(self, edit: Edit) -> str:
"""Generate a diff-style preview of the edit."""
lines = []
lines.append("=" * 80)
lines.append("[EDIT PREVIEW]")
lines.append("")
lines.append(f"Query: {edit.query}")
lines.append("")
lines.append("Proposed changes:")
lines.append("-" * 80)
# Show response as additions
for line in edit.response.split('\n'):
lines.append(f"+ {line}")
lines.append("-" * 80)
lines.append("")
lines.append("Commands:")
lines.append(" Ctrl+Y - Accept this edit")
lines.append(" Ctrl+N - Reject this edit")
lines.append(" Ctrl+A - Accept all pending")
lines.append(" Ctrl+R - Reject all pending")
lines.append("=" * 80)
return '\n'.join(lines)
def apply_edits_to_buffer(self, buffer, edits: List[Edit]) -> int:
"""
Apply accepted edits to the buffer.
Returns count of edits applied.
"""
count = 0
for edit in edits:
if edit.applied and not edit.rejected:
# Append response to buffer
separator = "" * 80
buffer.lines.append("")
buffer.lines.append(separator)
buffer.lines.append("[Applied Edit]")
buffer.lines.append(f" Query: {edit.query}")
buffer.lines.append("")
for line in edit.response.split('\n'):
buffer.lines.append(f" {line}")
buffer.lines.append("")
buffer.lines.append(separator)
buffer.lines.append("")
count += 1
return count
def get_status_text(self, mode: int) -> str:
"""Get status text for the status bar."""
mode_names = ["manual", "on save", "always"]
mode_name = mode_names[mode]
pending = self.get_pending_count()
if pending > 0:
return f"{mode_name} ({pending} pending)"
else:
return mode_name