Java 如何设计撤销&;在文本编辑器中重做?

Java 如何设计撤销&;在文本编辑器中重做?,java,text-editor,undo,undo-redo,redo,Java,Text Editor,Undo,Undo Redo,Redo,我的项目的一部分是编写一个文本编辑器,用于键入一些规则、编译应用程序并运行它。编写编译器已结束并发布测试版。在最终版本中,我们必须向文本编辑器添加撤消和重做。我使用一个文件,并定期将其保存到文本编辑器中。如何设计对我的文本编辑器的撤消和重做?文件的持久化结构发生了什么变化?您可以通过两种方式进行更改: 在列表中保留编辑器状态列表和指针;撤消将指针向后移动并恢复状态,重做则向前移动,执行某些操作会丢弃指针之外的所有内容,并将状态作为新的顶部元素插入 不要保留状态,而是保留动作,这要求你对每一个动

我的项目的一部分是编写一个文本编辑器,用于键入一些规则、编译应用程序并运行它。编写编译器已结束并发布测试版。在最终版本中,我们必须向文本编辑器添加撤消和重做。我使用一个文件,并定期将其保存到文本编辑器中。如何设计对我的文本编辑器的撤消和重做?文件的持久化结构发生了什么变化?

您可以通过两种方式进行更改:

  • 在列表中保留编辑器状态列表和指针;撤消将指针向后移动并恢复状态,重做则向前移动,执行某些操作会丢弃指针之外的所有内容,并将状态作为新的顶部元素插入
  • 不要保留状态,而是保留动作,这要求你对每一个动作都有一个抵消,以消除该动作的影响
在my(diagram)editor中,有四个级别的状态更改:

  • 动作片段:它们是更大动作的一部分,不能单独撤消或重做 (例如,移动鼠标)
  • 动作:一个或多个动作片段,形成有意义的更改,可以撤消或重做, 但在磁盘上更改后,这些内容不会反映在已编辑的文档中 (例如,选择元素)
  • 文档更改:一个或多个操作,用于更改编辑后的文档,使其保存到磁盘 (例如,更改、添加或删除元素)
  • 文档保存:文档的当前状态被显式保存到磁盘-此时,我的编辑器丢弃了撤消历史记录,因此您无法撤消保存后的操作

  • 读一本书。就我所记得的,有一个很好的例子。

    这是我的一份工作。

    哇,真是个巧合-我在最后一个小时在我的WYSIWYG文本编辑器中实现了撤销/重做:

    其基本思想是将文本编辑器的全部内容保存在一个数组中,或者保存上一次编辑之间的差异

    在有效点更新此数组,即每隔几个字符更新一次(检查每个按键的内容长度,如果其差异超过20个字符,则创建一个保存点)。此外,在样式设置(如果是富文本)、添加图像(如果允许)、粘贴文本等方面也会发生变化。您还需要一个指针(只是一个int变量)来指向数组中的哪个项是编辑器的当前状态)

    使数组具有设置的长度。每次添加保存点时,将其添加到数组的开头,并将所有其他数据点向下移动一个。(当您有这么多保存点时,阵列中的最后一项将被遗忘)

    当用户按下“撤消”按钮时,检查编辑器的当前内容是否与最新保存的内容相同(如果不相同,则说明用户自上次保存点以来进行了更改,因此保存编辑器的当前内容(以便可以重做),使编辑器等于上次保存点,并使指针变量=1(数组中的第二项)。如果它们相同,则自上次保存点以来未进行任何更改,因此您需要撤消到该点之前的点。为此,请将指针值+1递增,并使编辑器的内容=指针的值

    要重做,只需将指针值减少1并加载数组的内容(确保检查是否已到达数组的末尾)

    如果用户在撤消后进行编辑,则将定点值数组单元格向上移动到单元格0,并将其余单元格向上移动相同的量(一旦他们进行了不同的编辑,您不希望重做其他内容)

    另一个主要要点是,确保仅在文本编辑器的内容实际发生更改时才添加保存点(否则会得到重复的保存点,并且看起来撤消对用户没有任何作用)

    我无法帮助您了解java细节,但我很乐意回答您提出的任何其他问题


    Nico

    您可以将您的操作建模为两个堆栈。一个用于撤消,另一个用于重做。您可以使用命令创建更多高级命令,例如,当您要撤消宏的操作时;或者如果您要在一个操作中对单个单词或短语的单个击键进行分组

    编辑器中的每个操作(或重做操作)都会生成一个新的进入撤消堆栈的撤消命令(并清除重做堆栈)。每个撤消操作都会生成进入重做堆栈的相应重做命令


    您还可以,如注释中所述,将undo和redo命令组合成一种命令类型,该命令知道如何撤消和重做其操作。

    基本上有两种好方法:

    • “命令”设计模式

    • 仅在不可变对象上使用OO,其中所有内容都是由不可变对象构成的不可变对象,而这些对象本身又是由不可变对象构成的(这不太常见,但如果操作正确,则非常优雅)

    与naive命令或naive undo/redo相比,在不可变对象上使用OO的优势在于,您不需要考虑太多:不需要“撤消”操作的效果,也不需要“重放”所有命令。您只需要一个指向大量不可变对象列表的指针

    因为对象是不可变的,所以所有的“状态”都可以非常轻量级,因为您可以在任何状态下缓存/重用大多数对象

    “不可变对象之上的OO”是一颗纯粹的宝石。也许在接下来的10年内不会成为主流,也就是说;)


    附言:在不可变对象上执行OO也惊人地简化了并发编程。

    这里有一个片段,展示了SWT如何支持撤消/重做操作。以它为例(或者直接使用它,如果编辑器基于SWT):


    如果您不想要任何新奇的东西,您可以添加一个。您的
    文档
    将在每次添加或删除文本时触发一个
    UndoableEdit
    。若要撤消并重做每次更改,请