Pulpcode

捕获,搅碎,拼接,吞咽

0%

编辑器的撤销和重做如何实现

我一直好奇,编辑器的undoredo是如何实现的,而且因为不了解uodoredo工作原理到底是怎样的。常常会发现它并不能我所想的去工作。

这篇文章用一种模型解释undo和redo,我在编辑器上试过这种思路是没问题的,但我不能把握这种模型一定是对的,如果在以后的学习中发现还有问题,我会进一步修改。

清晰的思路

首先要明白,为什么有的时候我们点击redo是没用的,因为只有undo的才能redo,你在一个新操作上redo是没用的。

而且,虽然看上去在操作过程中,我们是在保存每一次的状态,但是在底层实现中,程序员是不会蠢到把每一次的缓冲区都保存为状态,真实情况就是只保存变化。这还真算是编程里面的一个范式了。版本管理器的原理也是如此。

还有我们要明白,任何一个操作都是可以被反转义为一个逆操作。比如“在xxx位置插入”abc”字符”的逆操作就是“将xxx位置到len(”abc”)长度的字符删除”。

构建模型

undoredo的模型类似于下图:

undo-redo

我们的每一次操作,就被放到一个类似栈的容器中(说类似,是因为它的属性和栈并不完全相同)。我们把编辑器底层维护的这块字符内存成为缓冲区,而在栈中存放的都是类似的操作:

在缓冲区的xx位置插入"abc"。
将缓冲区xx到yy位置的字符串删除。
......

分析操作

对缓冲区进行新的操作(current指针在栈顶)

当进行一个新的操作时,会将这个操作压栈。并将一个current指针指向它。

undo撤销

当我们撤销操作时,会将current指向的操作,做一个反转义,将生成的逆向操作在缓冲区中执行,并将current向栈底的移动一位。(注意,刚才的那个操作并没有出栈,只是移动current指针而已)

redo重做

当我们重做操作时,会将current指针栈顶的方向移动一位,并将指向的操作在缓冲区中执行。

对缓冲区进行新的操作(current指针不在栈顶)

那么如果我们在uodo几次后,直接对缓冲区直接进行操作呢?

这时将对current上方的所有操作出栈,将新的操作压栈,并将current指针指向它。

这也就是为什么,你在这时,redo操作是没用的。因为栈顶已经没东西了。