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

时间:2017-04-06 16:33:24

标签: javascript linked-list copy-paste undo-redo

我正在使用undomanager并尝试在双向链表上实现剪切/复制/粘贴功能。首先,这是我的代码。

Karma测试脚本:

    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。由于重复节点仅存在于剪贴板中而不存在于链表中,因此会出错。

我是否正确理解撤消/重做堆栈?写剪切/复制/粘贴的正​​确方法是什么?

1 个答案:

答案 0 :(得分:0)

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

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

我在调用剪切,复制和粘贴之间使用UndoManager的getIndex()函数发现了双重堆叠。