Overview
TheZepCommand system implements the Command pattern to provide undo/redo functionality for text editing operations. Each editing operation is encapsulated as a command object that can be executed, undone, and redone.
Source: include/zep/commands.h:8
Command Pattern Design
The command system follows the classic Gang of Four Command pattern:- Encapsulation: Each operation is wrapped in a command object
- Reversibility: Commands implement both
Redo()andUndo() - History: Commands can be stored in a stack for undo/redo
- Cursor tracking: Commands store cursor positions before and after execution
Base Class: ZepCommand
include/zep/commands.h:8-39
Constructor
currentMode- Reference to the buffer being editedcursorBefore- Cursor position before the command executescursorAfter- Cursor position after the command executes
include/zep/commands.h:11
Core Methods
Redo()
include/zep/commands.h:22
Undo()
include/zep/commands.h:23
Cursor Position Methods
GetCursorAfter()
include/zep/commands.h:25
GetCursorBefore()
include/zep/commands.h:29
Protected Members
include/zep/commands.h:35-38
Command Types
ZepCommand_GroupMarker
GroupMarker and EndGroup are treated as a single undoable unit.
Source: include/zep/commands.h:41
ZepCommand_EndGroup
include/zep/commands.h:52
ZepCommand_DeleteRange
m_startIndex- Beginning of the range to deletem_endIndex- End of the range to delete
include/zep/commands.h:63
ZepCommand_ReplaceRange
m_startIndex- Beginning of the range to replacem_endIndex- End of the range to replacem_strReplace- The replacement textm_mode- Replace mode flagsm_deleteStepChange- Records the deletion step for undo
include/zep/commands.h:76
ZepCommand_Insert
m_startIndex- Position to insert atm_strInsert- The text to insertm_endIndexInserted- Position after insertion (for undo)
include/zep/commands.h:93
Undo/Redo System
How It Works
- Command Creation: When the user performs an edit, a command object is created
- Execution: The command’s
Redo()method is called to perform the edit - History Stack: The command is pushed onto an undo stack
- Undo: Calling
Undo()reverses the command and moves it to the redo stack - Redo: Calling
Redo()reapplies the command and moves it back to the undo stack
Command Groups
Multiple individual commands can be grouped together usingZepCommand_GroupMarker and ZepCommand_EndGroup. This allows complex operations to be undone/redone as a single unit:
ChangeRecord
Them_changeRecord member tracks metadata about the change:
- What was modified
- Original values (for undo)
- Change locations
- Timestamps or sequence numbers
Example Usage
Grouped Command Example
Design Patterns and Best Practices
Command Pattern Benefits
- Separation of Concerns: Edit logic is separate from UI code
- History Management: Commands can be logged, replayed, or persisted
- Macro Recording: Command sequences can be recorded and replayed
- Testing: Commands can be unit tested independently
Cursor Position Tracking
Commands track cursor positions to ensure the cursor is correctly positioned after undo/redo:m_cursorBefore: Where the cursor was before the editm_cursorAfter: Where the cursor should be after the edit
Memory Management
Commands are typically managed with smart pointers (std::unique_ptr) in the undo/redo stacks. The buffer does not need to manage command lifetime.
Atomic Operations
ZepCommand_ReplaceRange demonstrates combining delete and insert into a single atomic command. This is more efficient than separate commands and provides better undo granularity.