我正在使用undomanager并尝试在双向链表上实现剪切/复制/粘贴功能。首先,这是我的代码。
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测试中的最后一个undo命令失败,出现以下错误:
Error: Unable to delete node with id 14914926779057942
此节点ID属于粘贴操作的重复节点:this.clipboard = pastedNode.duplicate()
undo管理器在重复节点的id上调用pasteNode undo函数中的deleteNode,而不是pastedNode。由于重复节点仅存在于剪贴板中而不存在于链表中,因此会出错。
我是否正确理解撤消/重做堆栈?写剪切/复制/粘贴的正确方法是什么?
答案 0 :(得分:0)
这里的问题是,当粘贴函数调用insertNode
时,撤消管理器的撤销和重做堆栈被双重堆叠。
例如,调用cutNode会调用deleteNode,这会在undo和redo堆栈上抛出一个命令。然后,在deleteNode返回后,cutNode会在两个堆栈上添加另一个层。
我在调用剪切,复制和粘贴之间使用UndoManager的getIndex()
函数发现了双重堆叠。