撤消粘贴操作时,撤消管理器堆栈似乎已损坏

Undo Manager stack seems to get corrupted when undoing a paste operation

我正在使用 undomanager 并尝试在双向链表上实现 cut/copy/paste 功能。首先,这是我的代码。

业力测试脚本:

    it('should cut and paste a node with working undo/redo', function () {
        var list = listFactory.createList();

        var node1 = list.appendNode(
            nodeFactory.createNode({
                //nodeinfo
            })
        );

        var node2 = list.appendNode(
            nodeFactory.createNode({
                //nodeinfo
            })
        );
        
        // here we cut the node2 out of the profile
        list.cutNode(node2.id);
        expect(list.clipboard.id).toBe(node2.id);
        expect(list.getLength()).toBe(2);

        // undoing the cut should clear the clipboard and return the node2 to where it was.
        list.undo();
        expect(list.clipboard).toBe(null);
        expect(list.getLength()).toBe(2);

        // redoing the cut should remove node2
        list.redo();
        expect(list.clipboard.id).toBe(node2.id);
        expect(list.getLength()).toBe(1);

        // pasting node2 in front of node1
        list.pasteNode(indexSeg.id);

        expect(list.getLength()).toBe(2);

        // the first undo should remove node2 from the front
        list.undo();
        expect(list.getLength()).toBe(1);
        
        // this should reset the list back to its original state
        list.undo(); // THIS COMMAND FAILS
    });

列出具有函数的对象

var List = function () {
 this.clipboard = null;
 this.head = null;
 this.tail = null;
 this.head.next = tail;
 this.tail.prev = head;
 // blahblah typical linked list stuff
};


// Inserts a node in front of the node with nodeId
List.prototype.insertNode = function(node, nodeId) {
 this.insertAt(node, nodeId);
 var list = this;
 this.undoManager.add({
  undo: function() {
   list.deleteNode(newNode.id);
  },
  redo: function() {
   list.insertNode(node, nodeId);
  }
 });
 return node;
};

// put node at tail
List.prototype.appendNode = function(node) {
 this.insertAt(node, null);
 var list = this;

 this.undoManager.add({
  undo: function() {
   list.deleteNode(node.id);
  },
  redo: function() {
   list.appendNode(node);
  }
 });
 return node;
};

// delete node with nodeId
List.prototype.deleteNode = function(nodeId) {
 var nextId = this.getNextNodeId(nodeId); // returns null if nodeId is at tail
 var deletedNode = this.deleteById(nodeId);
 var list = this;

 this.undoManager.add({
  undo: function() {
   //special case for handling last node
   if(!nextId)
    list.appendNode(deletedNode);
   else
    list.insertNode(deletedNode, nextId);
  },
  redo: function() {
   list.deleteNode(nodeId);
  }
 });

 return deletedNode;
};


// Removes the node with nodeId from list and stores it in clipboard
List.prototype.cutNode = function (nodeId) {
 var nextNodeId = this.getNextNodeId(nodeId); // returns null if nodeId is tail
 var cuttNode = this.deletedNode(nodeId);
 var oldClipboard = this.clipboard;
 this.clipboard = cuttNode;
 var list = this;

 this.undoManager.add({
  undo: function() {
   if (!nextNodeId) {
    list.appendNode(cuttNode);
   } else {
    list.insertNode(cuttNode, nextNodeId);
   }
   list.clipboard = oldClipboard;
  },
  redo: function() {
   list.cutNode(nodeId);
  }
 });
};

// duplicate node with nodeId and store in clipboard
List.prototype.copyNode = function (nodeId) {
 var node = this.
 var oldClipboard = this.clipboard;
 // duplicate() copies the node data to a new node object which generats a new/unique node id
 this.clipboard = node.duplicate();
 var list = this;

 this.undoManager.add({
  undo: function() {
   list.clipboard = oldClipboard;
  },
  redo: function() {
   list.clipboard = node;
  }
 });
};


// pastes clipboard node to list before nodeId
List.prototype.pasteNode = function (nodeId) {
 if (this.clipboard !== null) {
  var pastedNode = this.insertNode(this.clipboard, nodeId);
  // just in case we want to paste again, we need a unique node
  this.clipboard = pastedNode.duplicate(); 

  var list = this;

  this.undoManager.add({
   undo: function() {
    list.clipboard = list.deleteNode(pastedNode.id);
   },
   redo: function() {
    list.pasteNode(nodeId);
   }
  });
 }
};

karma 测试中的最后一个撤消命令失败并出现以下错误: Error: Unable to delete node with id 14914926779057942

此节点 ID 属于粘贴操作中的重复节点:this.clipboard = pastedNode.duplicate()

撤消管理器从复制节点的 id 上的 pasteNode 撤消函数调用 deleteNode,而不是 pastedNode。由于复制的节点只存在于剪贴板中,不存在于链表中,因此出错。

我是不是没有正确理解 undo/redo 堆栈? cut/copy/paste的正确写法是什么?

这里的问题是当粘贴函数调用 insertNode 时,撤消管理器的撤消和重做堆栈被双层堆叠。

例如,调用 cutNode 将调用 deleteNode,这将在撤消和重做堆栈上抛出一个命令。然后 cutNode 会在 deleteNode 返回后在两个堆栈上添加另一个层。

我通过在剪切、复制和粘贴调用之间使用 UndoManager 的 getIndex() 函数发现了双堆栈。