UNPKG

simple-text-buffer

Version:

A version of Atom's text-buffer with marker layers and file support removed

588 lines (542 loc) 22.4 kB
(function() { var BRANCHING_THRESHOLD, ChangeIterator, Leaf, Node, Patch, Point, RegionIterator, isEmpty, last, slice = [].slice; Point = require("./point"); last = function(array) { return array[array.length - 1]; }; isEmpty = function(node) { return node.inputExtent.isZero() && node.outputExtent.isZero(); }; BRANCHING_THRESHOLD = 3; Node = (function() { function Node(children) { this.children = children; this.calculateExtent(); } Node.prototype.splice = function(childIndex, splitChildren) { var inputOffset, leftMergeIndex, outputOffset, ref, ref1, rightMergeIndex, rightNeighbor, spliceChild, splitIndex, splitNodes; spliceChild = this.children[childIndex]; leftMergeIndex = rightMergeIndex = childIndex; if (splitChildren != null) { (ref = this.children).splice.apply(ref, [childIndex, 1].concat(slice.call(splitChildren))); childIndex += splitChildren.indexOf(spliceChild); rightMergeIndex += splitChildren.length - 1; } if (rightNeighbor = this.children[rightMergeIndex + 1]) { this.children[rightMergeIndex].merge(rightNeighbor); if (isEmpty(rightNeighbor)) { this.children.splice(rightMergeIndex + 1, 1); } } splitIndex = Math.ceil(this.children.length / BRANCHING_THRESHOLD); if (splitIndex > 1) { if (childIndex < splitIndex) { splitNodes = [this, new Node(this.children.splice(splitIndex))]; } else { splitNodes = [new Node(this.children.splice(0, splitIndex)), this]; childIndex -= splitIndex; } } ref1 = this.calculateExtent(childIndex), inputOffset = ref1.inputOffset, outputOffset = ref1.outputOffset; return { splitNodes: splitNodes, inputOffset: inputOffset, outputOffset: outputOffset, childIndex: childIndex }; }; Node.prototype.merge = function(rightNeighbor) { var childMerge, ref, ref1, result; childMerge = (ref = last(this.children)) != null ? ref.merge(rightNeighbor.children[0]) : void 0; if (isEmpty(rightNeighbor.children[0])) { rightNeighbor.children.shift(); } if (this.children.length + rightNeighbor.children.length <= BRANCHING_THRESHOLD) { this.inputExtent = this.inputExtent.traverse(rightNeighbor.inputExtent); this.outputExtent = this.outputExtent.traverse(rightNeighbor.outputExtent); (ref1 = this.children).push.apply(ref1, rightNeighbor.children); result = { inputExtent: rightNeighbor.inputExtent, outputExtent: rightNeighbor.outputExtent }; rightNeighbor.inputExtent = rightNeighbor.outputExtent = Point.ZERO; return result; } else if (childMerge != null) { this.inputExtent = this.inputExtent.traverse(childMerge.inputExtent); this.outputExtent = this.outputExtent.traverse(childMerge.outputExtent); rightNeighbor.inputExtent = rightNeighbor.inputExtent.traversalFrom(childMerge.inputExtent); rightNeighbor.outputExtent = rightNeighbor.outputExtent.traversalFrom(childMerge.outputExtent); return childMerge; } }; Node.prototype.calculateExtent = function(childIndex) { var child, i, j, len, ref, result; result = { inputOffset: null, outputOffset: null }; this.inputExtent = Point.ZERO; this.outputExtent = Point.ZERO; ref = this.children; for (i = j = 0, len = ref.length; j < len; i = ++j) { child = ref[i]; if (i === childIndex) { result.inputOffset = this.inputExtent; result.outputOffset = this.outputExtent; } this.inputExtent = this.inputExtent.traverse(child.inputExtent); this.outputExtent = this.outputExtent.traverse(child.outputExtent); } return result; }; Node.prototype.toString = function(indentLevel) { var i, indent, j, ref; if (indentLevel == null) { indentLevel = 0; } indent = ""; for (i = j = 0, ref = indentLevel; j < ref; i = j += 1) { indent += " "; } return indent + "[Node " + this.inputExtent + " " + this.outputExtent + "]\n" + (this.children.map(function(c) { return c.toString(indentLevel + 2); }).join("\n")); }; return Node; })(); Leaf = (function() { function Leaf(inputExtent1, outputExtent, content1) { this.inputExtent = inputExtent1; this.outputExtent = outputExtent; this.content = content1; } Leaf.prototype.insert = function(inputOffset, outputOffset, newInputExtent, newOutputExtent, newContent) { var inputExtentAfterOffset, outputExtentAfterOffset, splitNodes; inputExtentAfterOffset = this.inputExtent.traversalFrom(inputOffset); outputExtentAfterOffset = this.outputExtent.traversalFrom(outputOffset); if (this.content != null) { this.inputExtent = inputOffset.traverse(newInputExtent).traverse(inputExtentAfterOffset); this.outputExtent = outputOffset.traverse(newOutputExtent).traverse(outputExtentAfterOffset); this.content = this.content.slice(0, outputOffset.column) + newContent + this.content.slice(outputOffset.column); inputOffset = inputOffset.traverse(newInputExtent); outputOffset = outputOffset.traverse(newOutputExtent); } else if (newInputExtent.isPositive() || newOutputExtent.isPositive()) { splitNodes = []; if (outputOffset.isPositive()) { splitNodes.push(new Leaf(inputOffset, outputOffset, null)); } this.inputExtent = newInputExtent; this.outputExtent = newOutputExtent; this.content = newContent; splitNodes.push(this); if (outputExtentAfterOffset.isPositive()) { splitNodes.push(new Leaf(inputExtentAfterOffset, outputExtentAfterOffset, null)); } inputOffset = this.inputExtent; outputOffset = this.outputExtent; } return { splitNodes: splitNodes, inputOffset: inputOffset, outputOffset: outputOffset }; }; Leaf.prototype.merge = function(rightNeighbor) { var ref, ref1, result; if (((this.content != null) === (rightNeighbor.content != null)) || isEmpty(this) || isEmpty(rightNeighbor)) { this.outputExtent = this.outputExtent.traverse(rightNeighbor.outputExtent); this.inputExtent = this.inputExtent.traverse(rightNeighbor.inputExtent); this.content = ((ref = this.content) != null ? ref : "") + ((ref1 = rightNeighbor.content) != null ? ref1 : ""); if (this.content === "" && this.outputExtent.isPositive()) { this.content = null; } result = { inputExtent: rightNeighbor.inputExtent, outputExtent: rightNeighbor.outputExtent }; rightNeighbor.inputExtent = rightNeighbor.outputExtent = Point.ZERO; rightNeighbor.content = null; return result; } }; Leaf.prototype.toString = function(indentLevel) { var i, indent, j, ref; if (indentLevel == null) { indentLevel = 0; } indent = ""; for (i = j = 0, ref = indentLevel; j < ref; i = j += 1) { indent += " "; } if (this.content != null) { return indent + "[Leaf " + this.inputExtent + " " + this.outputExtent + " " + (JSON.stringify(this.content)) + "]"; } else { return indent + "[Leaf " + this.inputExtent + " " + this.outputExtent + "]"; } }; return Leaf; })(); RegionIterator = (function() { function RegionIterator(patch, path) { this.patch = patch; this.path = path; if (this.path == null) { this.path = []; this.descendToLeftmostLeaf(this.patch.rootNode); } } RegionIterator.prototype.next = function() { var entry, nextChild, parentEntry, ref, ref1, value; while ((entry = last(this.path)) && entry.inputOffset.isEqual(entry.node.inputExtent) && entry.outputOffset.isEqual(entry.node.outputExtent)) { this.path.pop(); if (parentEntry = last(this.path)) { parentEntry.childIndex++; parentEntry.inputOffset = parentEntry.inputOffset.traverse(entry.inputOffset); parentEntry.outputOffset = parentEntry.outputOffset.traverse(entry.outputOffset); if (nextChild = parentEntry.node.children[parentEntry.childIndex]) { this.descendToLeftmostLeaf(nextChild); entry = last(this.path); } } else { this.path.push(entry); return { value: null, done: true }; } } value = (ref = (ref1 = entry.node.content) != null ? ref1.slice(entry.outputOffset.column) : void 0) != null ? ref : null; entry.outputOffset = entry.node.outputExtent; entry.inputOffset = entry.node.inputExtent; return { value: value, done: false }; }; RegionIterator.prototype.seek = function(targetOutputOffset) { var child, childIndex, childInputEnd, childInputStart, childOutputEnd, childOutputStart, inputOffset, j, len, node, outputOffset, ref; this.path.length = 0; node = this.patch.rootNode; while (true) { if (node.children != null) { childInputEnd = Point.ZERO; childOutputEnd = Point.ZERO; ref = node.children; for (childIndex = j = 0, len = ref.length; j < len; childIndex = ++j) { child = ref[childIndex]; childInputStart = childInputEnd; childOutputStart = childOutputEnd; childInputEnd = childInputStart.traverse(child.inputExtent); childOutputEnd = childOutputStart.traverse(child.outputExtent); if (childOutputEnd.compare(targetOutputOffset) >= 0) { inputOffset = childInputStart; outputOffset = childOutputStart; this.path.push({ node: node, childIndex: childIndex, inputOffset: inputOffset, outputOffset: outputOffset }); targetOutputOffset = targetOutputOffset.traversalFrom(childOutputStart); node = child; break; } } } else { if (targetOutputOffset.isEqual(node.outputExtent)) { inputOffset = node.inputExtent; } else { inputOffset = Point.min(node.inputExtent, targetOutputOffset); } outputOffset = targetOutputOffset; childIndex = null; this.path.push({ node: node, inputOffset: inputOffset, outputOffset: outputOffset, childIndex: childIndex }); break; } } return this; }; RegionIterator.prototype.seekToInputPosition = function(targetInputOffset) { var child, childIndex, childInputEnd, childInputStart, childOutputEnd, childOutputStart, inputOffset, j, len, node, outputOffset, ref; this.path.length = 0; node = this.patch.rootNode; while (true) { if (node.children != null) { childInputEnd = Point.ZERO; childOutputEnd = Point.ZERO; ref = node.children; for (childIndex = j = 0, len = ref.length; j < len; childIndex = ++j) { child = ref[childIndex]; childInputStart = childInputEnd; childOutputStart = childOutputEnd; childInputEnd = childInputStart.traverse(child.inputExtent); childOutputEnd = childOutputStart.traverse(child.outputExtent); if (childInputEnd.compare(targetInputOffset) >= 0) { inputOffset = childInputStart; outputOffset = childOutputStart; this.path.push({ node: node, childIndex: childIndex, inputOffset: inputOffset, outputOffset: outputOffset }); targetInputOffset = targetInputOffset.traversalFrom(childInputStart); node = child; break; } } } else { inputOffset = targetInputOffset; if (targetInputOffset.isEqual(node.inputExtent)) { outputOffset = node.outputExtent; } else { outputOffset = Point.min(node.outputExtent, targetInputOffset); } childIndex = null; this.path.push({ node: node, inputOffset: inputOffset, outputOffset: outputOffset, childIndex: childIndex }); break; } } return this; }; RegionIterator.prototype.splice = function(oldOutputExtent, newExtent, newContent) { var inputExtent, rightEdge; rightEdge = this.copy().seek(this.getOutputPosition().traverse(oldOutputExtent)); inputExtent = rightEdge.getInputPosition().traversalFrom(this.getInputPosition()); this.deleteUntil(rightEdge); return this.insert(inputExtent, newExtent, newContent); }; RegionIterator.prototype.getOutputPosition = function() { var entry, j, len, ref, result; result = Point.ZERO; ref = this.path; for (j = 0, len = ref.length; j < len; j++) { entry = ref[j]; result = result.traverse(entry.outputOffset); } return result; }; RegionIterator.prototype.getInputPosition = function() { var inputOffset, j, len, node, outputOffset, ref, ref1, result; result = Point.ZERO; ref = this.path; for (j = 0, len = ref.length; j < len; j++) { ref1 = ref[j], node = ref1.node, inputOffset = ref1.inputOffset, outputOffset = ref1.outputOffset; result = result.traverse(inputOffset); } return result; }; RegionIterator.prototype.copy = function() { return new RegionIterator(this.patch, this.path.slice()); }; RegionIterator.prototype.descendToLeftmostLeaf = function(node) { var entry, results; results = []; while (true) { entry = { node: node, outputOffset: Point.ZERO, inputOffset: Point.ZERO, childIndex: null }; this.path.push(entry); if (node.children != null) { entry.childIndex = 0; results.push(node = node.children[0]); } else { break; } } return results; }; RegionIterator.prototype.deleteUntil = function(rightIterator) { var childIndex, i, inputOffset, j, k, left, meetingIndex, node, outputOffset, ref, ref1, ref2, ref3, right, spliceIndex, totalInputOffset, totalOutputOffset; meetingIndex = null; totalInputOffset = Point.ZERO; totalOutputOffset = Point.ZERO; ref = this.path; for (i = j = ref.length - 1; j >= 0; i = j += -1) { ref1 = ref[i], node = ref1.node, inputOffset = ref1.inputOffset, outputOffset = ref1.outputOffset, childIndex = ref1.childIndex; if (node === rightIterator.path[i].node) { meetingIndex = i; break; } if (node.content != null) { node.content = node.content.slice(0, outputOffset.column); } else if (node.children != null) { node.children.splice(childIndex + 1); } totalInputOffset = inputOffset.traverse(totalInputOffset); totalOutputOffset = outputOffset.traverse(totalOutputOffset); node.inputExtent = totalInputOffset; node.outputExtent = totalOutputOffset; } totalInputOffset = Point.ZERO; totalOutputOffset = Point.ZERO; ref2 = rightIterator.path; for (i = k = ref2.length - 1; k >= 0; i = k += -1) { ref3 = ref2[i], node = ref3.node, inputOffset = ref3.inputOffset, outputOffset = ref3.outputOffset, childIndex = ref3.childIndex; if (i === meetingIndex) { break; } if (node.content != null) { node.content = node.content.slice(outputOffset.column); } else if (node.children != null) { if (isEmpty(node.children[childIndex])) { node.children.splice(childIndex, 1); } node.children.splice(0, childIndex); } totalInputOffset = inputOffset.traverse(totalInputOffset); totalOutputOffset = outputOffset.traverse(totalOutputOffset); node.inputExtent = node.inputExtent.traversalFrom(totalInputOffset); node.outputExtent = node.outputExtent.traversalFrom(totalOutputOffset); } left = this.path[meetingIndex]; right = rightIterator.path[meetingIndex]; node = left.node; node.outputExtent = left.outputOffset.traverse(node.outputExtent.traversalFrom(right.outputOffset)); node.inputExtent = left.inputOffset.traverse(node.inputExtent.traversalFrom(right.inputOffset)); if (node.content != null) { node.content = node.content.slice(0, left.outputOffset.column) + node.content.slice(right.outputOffset.column); } else if (node.children != null) { spliceIndex = left.childIndex + 1; if (isEmpty(node.children[right.childIndex])) { node.children.splice(right.childIndex, 1); } node.children.splice(spliceIndex, right.childIndex - spliceIndex); } return this; }; RegionIterator.prototype.insert = function(newInputExtent, newOutputExtent, newContent) { var childIndex, entry, inputOffset, j, newPath, node, outputOffset, ref, ref1, ref2, ref3, ref4, ref5, splitNodes; newPath = []; splitNodes = null; ref = this.path; for (j = ref.length - 1; j >= 0; j += -1) { ref1 = ref[j], node = ref1.node, inputOffset = ref1.inputOffset, outputOffset = ref1.outputOffset, childIndex = ref1.childIndex; if (node instanceof Leaf) { ref2 = node.insert(inputOffset, outputOffset, newInputExtent, newOutputExtent, newContent), splitNodes = ref2.splitNodes, inputOffset = ref2.inputOffset, outputOffset = ref2.outputOffset; } else { ref3 = node.splice(childIndex, splitNodes), splitNodes = ref3.splitNodes, inputOffset = ref3.inputOffset, outputOffset = ref3.outputOffset, childIndex = ref3.childIndex; } newPath.unshift({ node: node, inputOffset: inputOffset, outputOffset: outputOffset, childIndex: childIndex }); } if (splitNodes != null) { node = this.patch.rootNode = new Node([node]); ref4 = node.splice(0, splitNodes), inputOffset = ref4.inputOffset, outputOffset = ref4.outputOffset, childIndex = ref4.childIndex; newPath.unshift({ node: node, inputOffset: inputOffset, outputOffset: outputOffset, childIndex: childIndex }); } while (((ref5 = this.patch.rootNode.children) != null ? ref5.length : void 0) === 1) { this.patch.rootNode = this.patch.rootNode.children[0]; newPath.shift(); } entry = last(newPath); if (entry.outputOffset.isEqual(entry.node.outputExtent)) { entry.inputOffset = entry.node.inputExtent; } else { entry.inputOffset = Point.min(entry.node.inputExtent, entry.outputOffset); } this.path = newPath; return this; }; RegionIterator.prototype.toString = function() { var childIndex, entries, inputOffset, node, outputOffset; entries = (function() { var j, len, ref, ref1, results; ref = this.path; results = []; for (j = 0, len = ref.length; j < len; j++) { ref1 = ref[j], node = ref1.node, inputOffset = ref1.inputOffset, outputOffset = ref1.outputOffset, childIndex = ref1.childIndex; results.push(" {inputOffset:" + inputOffset + ", outputOffset:" + outputOffset + ", childIndex:" + childIndex + "}"); } return results; }).call(this); return "[RegionIterator\n" + (entries.join("\n")) + "]"; }; return RegionIterator; })(); ChangeIterator = (function() { function ChangeIterator(patchIterator) { this.patchIterator = patchIterator; this.inputPosition = Point.ZERO; this.outputPosition = Point.ZERO; } ChangeIterator.prototype.next = function() { var content, lastInputPosition, lastOutputPosition, newExtent, next, oldExtent, position; while (!(next = this.patchIterator.next()).done) { lastInputPosition = this.inputPosition; lastOutputPosition = this.outputPosition; this.inputPosition = this.patchIterator.getInputPosition(); this.outputPosition = this.patchIterator.getOutputPosition(); if ((content = next.value) != null) { position = lastOutputPosition; oldExtent = this.inputPosition.traversalFrom(lastInputPosition); newExtent = this.outputPosition.traversalFrom(lastOutputPosition); return { done: false, value: { position: position, oldExtent: oldExtent, newExtent: newExtent, content: content } }; } } return { done: true, value: null }; }; return ChangeIterator; })(); module.exports = Patch = (function() { function Patch() { this.clear(); } Patch.prototype.splice = function(spliceOutputStart, oldOutputExtent, newOutputExtent, content) { var iterator; iterator = this.regions(); iterator.seek(spliceOutputStart); return iterator.splice(oldOutputExtent, newOutputExtent, content); }; Patch.prototype.clear = function() { return this.rootNode = new Leaf(Point.INFINITY, Point.INFINITY, null); }; Patch.prototype.regions = function() { return new RegionIterator(this); }; Patch.prototype.changes = function() { return new ChangeIterator(this.regions()); }; Patch.prototype.toInputPosition = function(outputPosition) { return this.regions().seek(outputPosition).getInputPosition(); }; Patch.prototype.toOutputPosition = function(inputPosition) { return this.regions().seekToInputPosition(inputPosition).getOutputPosition(); }; return Patch; })(); }).call(this);