第 5 章 · 05 撤销重做与综合:一个能编辑保存的小编辑器 本节摘要:这是第 5 章的收尾节,讲两件事:撤销/重做的实现(命令模式的应用),以及把前四节组装成一个能编辑、保存、撤销的小编辑器。撤销的本质是「把每次编辑封装成可逆命令」——插入的反向是删除、删除的反向是重新插入。理解命令模式,你不仅会做编辑器撤销,更掌握了一门通用的软件设计模式。 内容来源:基于编辑器设计与设计模式整理的导读。 学习目标 阅读完本节,你应当能够: 说清撤销的本质:把每次编辑封装成可逆命令。 实现撤销栈/重做栈的双栈模型。 理解「命令模式」(command pattern)是通用设计模式。 把缓冲区、光标、模式、文件 IO、渲染、撤销组装成一个最小可用编辑器。
本节摘要:这是第 5 章的收尾节,讲两件事:撤销/重做的实现(命令模式的应用),以及把前四节组装成一个能编辑、保存、撤销的小编辑器。撤销的本质是「把每次编辑封装成可逆命令」——插入的反向是删除、删除的反向是重新插入。理解命令模式,你不仅会做编辑器撤销,更掌握了一门通用的软件设计模式。
内容来源:基于编辑器设计与设计模式整理的导读。
阅读完本节,你应当能够:
撤销/重做不只为编辑器——它是「命令模式」的典型应用,这种模式用在任何需要「可逆操作」的场景:数据库事务回滚、图形编辑器、游戏、协同编辑。理解它,你掌握了一门通用设计模式。
每次编辑封装成一个命令对象,含「执行」与「撤销」两个方法:
InsertCommand { pos, text }: do(): 在 pos 插入 text undo(): 在 pos 删除 len(text) 个字符 DeleteCommand { pos, text }: do(): 在 pos 删除 text undo(): 在 pos 重新插入 text(删掉的存起来)
维护两个栈:undo_stack 与 redo_stack。
执行命令: cmd.do(); undo_stack.push(cmd); 清空 redo_stack (新操作后, 重做历史失效) 撤销 (Ctrl+Z): cmd = undo_stack.pop(); cmd.undo(); redo_stack.push(cmd) 重做 (Ctrl+Y): cmd = redo_stack.pop(); cmd.do(); undo_stack.push(cmd)
把前四节组装:
1. 启动: enter_raw_mode(); atexit(restore); 读文件到缓冲区 2. 主循环: render() # 清屏 + 画缓冲 + 移光标(04 节) key = read_key() # 读按键(04 节) if normal 模式: hjkl/gg/G/dd/... → 改光标或执行编辑命令(02 节) i/a/o → 切 insert elif insert 模式: 字符 → 插入缓冲(封装为 InsertCommand, 进 undo 栈)(01 节) Esc → 切 normal 退格 → 删除(封装 DeleteCommand) Ctrl+S → 原子保存(03 节) Ctrl+Z → undo; Ctrl+Y → redo(本节) Ctrl+Q → 退出
这是最小可用:能打开、编辑、保存、撤销。后续可扩展搜索替换(用第 4 章正则)、多缓冲、语法高亮。
至此第 5 章「造文本编辑器」全部讲完。本章让你造一个能打开、编辑、保存、撤销的小编辑器,并理解 Vim/Emacs 的设计智慧。下一章造 Git——理解版本控制的内部模型。