fix: Use Alt+U/Alt+R for undo/redo, fix state tracking
- Changed undo/redo keys to Alt+U and Alt+R (meta keys work in urwid) - Fixed undo logic: now saves state BEFORE edit happens, not after - Added duplicate state prevention - Simplified the tracking logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
67a83e5379
commit
f84e06aa05
|
|
@ -345,11 +345,9 @@ class UndoableEdit(urwid.Edit):
|
||||||
"""A multiline Edit with undo/redo support.
|
"""A multiline Edit with undo/redo support.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Undo with Ctrl+U (up to 50 states)
|
- Undo with Alt+U (up to 50 states)
|
||||||
- Redo with Ctrl+R
|
- Redo with Alt+R
|
||||||
- Tab passes through for focus cycling
|
- Tab passes through for focus cycling
|
||||||
|
|
||||||
Note: Ctrl+Z cannot be used as it's the Unix suspend signal (SIGTSTP).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAX_UNDO = 50 # Maximum undo history size
|
MAX_UNDO = 50 # Maximum undo history size
|
||||||
|
|
@ -358,40 +356,40 @@ class UndoableEdit(urwid.Edit):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._undo_stack = [] # List of (text, cursor_pos) tuples
|
self._undo_stack = [] # List of (text, cursor_pos) tuples
|
||||||
self._redo_stack = []
|
self._redo_stack = []
|
||||||
self._last_saved_text = self.edit_text # Track for change detection
|
|
||||||
|
|
||||||
def keypress(self, size, key):
|
def keypress(self, size, key):
|
||||||
if key in ('tab', 'shift tab'):
|
if key in ('tab', 'shift tab'):
|
||||||
return key
|
return key
|
||||||
|
|
||||||
# Save state before any editing operation (for undo)
|
# Handle undo (Alt+U or meta u)
|
||||||
current_text = self.edit_text
|
if key in ('meta u', 'alt u'):
|
||||||
if current_text != self._last_saved_text:
|
|
||||||
self._save_undo_state(self._last_saved_text, self.edit_pos)
|
|
||||||
self._last_saved_text = current_text
|
|
||||||
|
|
||||||
# Handle undo (Ctrl+U) - can't use Ctrl+Z as it's Unix suspend signal
|
|
||||||
if key == 'ctrl u':
|
|
||||||
self._undo()
|
self._undo()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Handle redo (Ctrl+R)
|
# Handle redo (Alt+R or meta r)
|
||||||
if key == 'ctrl r':
|
if key in ('meta r', 'alt r'):
|
||||||
self._redo()
|
self._redo()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Save current state BEFORE the edit for undo
|
||||||
|
old_text = self.edit_text
|
||||||
|
old_pos = self.edit_pos
|
||||||
|
|
||||||
|
# Let the parent handle the keypress
|
||||||
result = super().keypress(size, key)
|
result = super().keypress(size, key)
|
||||||
|
|
||||||
# After edit, check if text changed and save for potential undo
|
# If text changed, save the old state to undo stack
|
||||||
new_text = self.edit_text
|
if self.edit_text != old_text:
|
||||||
if new_text != self._last_saved_text:
|
self._save_undo_state(old_text, old_pos)
|
||||||
self._redo_stack.clear() # Clear redo on new edit
|
self._redo_stack.clear() # Clear redo on new edit
|
||||||
self._last_saved_text = new_text
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _save_undo_state(self, text, pos):
|
def _save_undo_state(self, text, pos):
|
||||||
"""Save current state to undo stack."""
|
"""Save state to undo stack."""
|
||||||
|
# Don't save duplicate states
|
||||||
|
if self._undo_stack and self._undo_stack[-1][0] == text:
|
||||||
|
return
|
||||||
if len(self._undo_stack) >= self.MAX_UNDO:
|
if len(self._undo_stack) >= self.MAX_UNDO:
|
||||||
self._undo_stack.pop(0)
|
self._undo_stack.pop(0)
|
||||||
self._undo_stack.append((text, pos))
|
self._undo_stack.append((text, pos))
|
||||||
|
|
@ -408,7 +406,6 @@ class UndoableEdit(urwid.Edit):
|
||||||
text, pos = self._undo_stack.pop()
|
text, pos = self._undo_stack.pop()
|
||||||
self.set_edit_text(text)
|
self.set_edit_text(text)
|
||||||
self.set_edit_pos(min(pos, len(text)))
|
self.set_edit_pos(min(pos, len(text)))
|
||||||
self._last_saved_text = text
|
|
||||||
|
|
||||||
def _redo(self):
|
def _redo(self):
|
||||||
"""Restore state from redo stack."""
|
"""Restore state from redo stack."""
|
||||||
|
|
@ -422,7 +419,6 @@ class UndoableEdit(urwid.Edit):
|
||||||
text, pos = self._redo_stack.pop()
|
text, pos = self._redo_stack.pop()
|
||||||
self.set_edit_text(text)
|
self.set_edit_text(text)
|
||||||
self.set_edit_pos(min(pos, len(text)))
|
self.set_edit_pos(min(pos, len(text)))
|
||||||
self._last_saved_text = text
|
|
||||||
|
|
||||||
|
|
||||||
class DOSScrollBar(urwid.WidgetWrap):
|
class DOSScrollBar(urwid.WidgetWrap):
|
||||||
|
|
@ -1687,7 +1683,7 @@ class SmartToolsUI:
|
||||||
default_file = existing.code_file if existing and existing.code_file else f"{default_output_var}.py"
|
default_file = existing.code_file if existing and existing.code_file else f"{default_output_var}.py"
|
||||||
file_edit = urwid.Edit(('label', "File: "), default_file)
|
file_edit = urwid.Edit(('label', "File: "), default_file)
|
||||||
|
|
||||||
# Multiline code editor with undo/redo (Ctrl+U / Ctrl+R)
|
# Multiline code editor with undo/redo (Alt+U / Alt+R)
|
||||||
default_code = existing.code if existing else f"{default_output_var} = input.upper()"
|
default_code = existing.code if existing else f"{default_output_var} = input.upper()"
|
||||||
code_edit = UndoableEdit(
|
code_edit = UndoableEdit(
|
||||||
edit_text=default_code,
|
edit_text=default_code,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue