我一直好奇,编辑器的undo
和redo
是如何实现的,而且因为不了解uodo
和redo
工作原理到底是怎样的。常常会发现它并不能我所想的去工作。
这篇文章用一种模型解释undo和redo,我在编辑器上试过这种思路是没问题的,但我不能把握这种模型一定是对的,如果在以后的学习中发现还有问题,我会进一步修改。
清晰的思路
首先要明白,为什么有的时候我们点击redo
是没用的,因为只有undo
的才能redo
,你在一个新操作上redo
是没用的。
而且,虽然看上去在操作过程中,我们是在保存每一次的状态,但是在底层实现中,程序员是不会蠢到把每一次的缓冲区都保存为状态,真实情况就是只保存变化
。这还真算是编程里面的一个范式了。版本管理器的原理也是如此。
还有我们要明白,任何一个操作都是可以被反转义为一个逆操作。比如“在xxx位置插入”abc”字符”的逆操作就是“将xxx位置到len(”abc”)长度的字符删除”。
构建模型
undo
和redo
的模型类似于下图:
我们的每一次操作,就被放到一个类似栈的容器中(说类似,是因为它的属性和栈并不完全相同)。我们把编辑器底层维护的这块字符内存成为缓冲区,而在栈中存放的都是类似的操作:
在缓冲区的xx位置插入"abc"。
将缓冲区xx到yy位置的字符串删除。
......
分析操作
对缓冲区进行新的操作(current指针在栈顶)
当进行一个新的操作时,会将这个操作压栈。并将一个current
指针指向它。
undo撤销
当我们撤销操作时,会将current
指向的操作,做一个反转义,将生成的逆向操作在缓冲区中执行,并将current
向栈底的移动一位。(注意,刚才的那个操作并没有出栈,只是移动current指针而已)
redo重做
当我们重做操作时,会将current指针栈顶的方向移动一位,并将指向的操作在缓冲区中执行。
对缓冲区进行新的操作(current指针不在栈顶)
那么如果我们在uodo
几次后,直接对缓冲区直接进行操作呢?
这时将对current
上方的所有操作出栈,将新的操作压栈,并将current
指针指向它。
这也就是为什么,你在这时,redo
操作是没用的。因为栈顶已经没东西了。