UNPKG

alm

Version:

The best IDE for TypeScript

711 lines (710 loc) 27.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var Debug = ts.Debug; /** * All the line stuff as it is from session.ts */ var lineCollectionCapacity = 4; var LineIndex = /** @class */ (function () { function LineIndex() { // set this to true to check each edit for accuracy this.checkEdits = false; } LineIndex.prototype.charOffsetToLineNumberAndPos = function (charOffset) { return this.root.charOffsetToLineNumberAndPos(1, charOffset); }; LineIndex.prototype.lineNumberToInfo = function (lineNumber) { var lineCount = this.root.lineCount(); if (lineNumber <= lineCount) { var lineInfo = this.root.lineNumberToInfo(lineNumber, 0); lineInfo.line = lineNumber; return lineInfo; } else { return { line: lineNumber, offset: this.root.charCount() }; } }; LineIndex.prototype.load = function (lines) { if (lines.length > 0) { var leaves = []; for (var i = 0, len = lines.length; i < len; i++) { leaves[i] = new LineLeaf(lines[i]); } this.root = LineIndex.buildTreeFromBottom(leaves); } else { this.root = new LineNode(); } }; LineIndex.prototype.walk = function (rangeStart, rangeLength, walkFns) { this.root.walk(rangeStart, rangeLength, walkFns); }; LineIndex.prototype.getText = function (rangeStart, rangeLength) { var accum = ""; if ((rangeLength > 0) && (rangeStart < this.root.charCount())) { this.walk(rangeStart, rangeLength, { goSubtree: true, done: false, leaf: function (relativeStart, relativeLength, ll) { accum = accum.concat(ll.text.substring(relativeStart, relativeStart + relativeLength)); } }); } return accum; }; LineIndex.prototype.getLength = function () { return this.root.charCount(); }; LineIndex.prototype.every = function (f, rangeStart, rangeEnd) { if (!rangeEnd) { rangeEnd = this.root.charCount(); } var walkFns = { goSubtree: true, done: false, leaf: function (relativeStart, relativeLength, ll) { if (!f(ll, relativeStart, relativeLength)) { this.done = true; } } }; this.walk(rangeStart, rangeEnd - rangeStart, walkFns); return !walkFns.done; }; LineIndex.prototype.edit = function (pos, deleteLength, newText) { function editFlat(source, s, dl, nt) { if (nt === void 0) { nt = ""; } return source.substring(0, s) + nt + source.substring(s + dl, source.length); } if (this.root.charCount() === 0) { // TODO: assert deleteLength === 0 if (newText) { this.load(LineIndex.linesFromText(newText).lines); return this; } } else { var checkText = void 0; if (this.checkEdits) { checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText); } var walker = new EditWalker(); if (pos >= this.root.charCount()) { // insert at end pos = this.root.charCount() - 1; var endString = this.getText(pos, 1); if (newText) { newText = endString + newText; } else { newText = endString; } deleteLength = 0; walker.suppressTrailingText = true; } else if (deleteLength > 0) { // check whether last characters deleted are line break var e = pos + deleteLength; var lineInfo = this.charOffsetToLineNumberAndPos(e); if ((lineInfo && (lineInfo.offset === 0))) { // move range end just past line that will merge with previous line deleteLength += lineInfo.text.length; // store text by appending to end of insertedText if (newText) { newText = newText + lineInfo.text; } else { newText = lineInfo.text; } } } if (pos < this.root.charCount()) { this.root.walk(pos, deleteLength, walker); walker.insertLines(newText); } if (this.checkEdits) { var updatedText = this.getText(0, this.root.charCount()); Debug.assert(checkText == updatedText, "buffer edit mismatch"); } return walker.lineIndex; } }; LineIndex.buildTreeFromBottom = function (nodes) { var nodeCount = Math.ceil(nodes.length / lineCollectionCapacity); var interiorNodes = []; var nodeIndex = 0; for (var i = 0; i < nodeCount; i++) { interiorNodes[i] = new LineNode(); var charCount = 0; var lineCount = 0; for (var j = 0; j < lineCollectionCapacity; j++) { if (nodeIndex < nodes.length) { interiorNodes[i].add(nodes[nodeIndex]); charCount += nodes[nodeIndex].charCount(); lineCount += nodes[nodeIndex].lineCount(); } else { break; } nodeIndex++; } interiorNodes[i].totalChars = charCount; interiorNodes[i].totalLines = lineCount; } if (interiorNodes.length === 1) { return interiorNodes[0]; } else { return this.buildTreeFromBottom(interiorNodes); } }; LineIndex.linesFromText = function (text) { var lineStarts = ts.computeLineStarts(text); if (lineStarts.length === 0) { return { lines: [], lineMap: lineStarts }; } var lines = new Array(lineStarts.length); var lc = lineStarts.length - 1; for (var lmi = 0; lmi < lc; lmi++) { lines[lmi] = text.substring(lineStarts[lmi], lineStarts[lmi + 1]); } var endText = text.substring(lineStarts[lc]); if (endText.length > 0) { lines[lc] = endText; } else { lines.length--; } return { lines: lines, lineMap: lineStarts }; }; return LineIndex; }()); exports.LineIndex = LineIndex; var LineNode = /** @class */ (function () { function LineNode() { this.totalChars = 0; this.totalLines = 0; this.children = []; } LineNode.prototype.isLeaf = function () { return false; }; LineNode.prototype.updateCounts = function () { this.totalChars = 0; this.totalLines = 0; for (var i = 0, len = this.children.length; i < len; i++) { var child = this.children[i]; this.totalChars += child.charCount(); this.totalLines += child.lineCount(); } }; LineNode.prototype.execWalk = function (rangeStart, rangeLength, walkFns, childIndex, nodeType) { if (walkFns.pre) { walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType); } if (walkFns.goSubtree) { this.children[childIndex].walk(rangeStart, rangeLength, walkFns); if (walkFns.post) { walkFns.post(rangeStart, rangeLength, this.children[childIndex], this, nodeType); } } else { walkFns.goSubtree = true; } return walkFns.done; }; LineNode.prototype.skipChild = function (relativeStart, relativeLength, childIndex, walkFns, nodeType) { if (walkFns.pre && (!walkFns.done)) { walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType); walkFns.goSubtree = true; } }; LineNode.prototype.walk = function (rangeStart, rangeLength, walkFns) { // assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars) var childIndex = 0; var child = this.children[0]; var childCharCount = child.charCount(); // find sub-tree containing start var adjustedStart = rangeStart; while (adjustedStart >= childCharCount) { this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart); adjustedStart -= childCharCount; child = this.children[++childIndex]; childCharCount = child.charCount(); } // Case I: both start and end of range in same subtree if ((adjustedStart + rangeLength) <= childCharCount) { if (this.execWalk(adjustedStart, rangeLength, walkFns, childIndex, CharRangeSection.Entire)) { return; } } else { // Case II: start and end of range in different subtrees (possibly with subtrees in the middle) if (this.execWalk(adjustedStart, childCharCount - adjustedStart, walkFns, childIndex, CharRangeSection.Start)) { return; } var adjustedLength = rangeLength - (childCharCount - adjustedStart); child = this.children[++childIndex]; childCharCount = child.charCount(); while (adjustedLength > childCharCount) { if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) { return; } adjustedLength -= childCharCount; child = this.children[++childIndex]; childCharCount = child.charCount(); } if (adjustedLength > 0) { if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) { return; } } } // Process any subtrees after the one containing range end if (walkFns.pre) { var clen = this.children.length; if (childIndex < (clen - 1)) { for (var ej = childIndex + 1; ej < clen; ej++) { this.skipChild(0, 0, ej, walkFns, CharRangeSection.PostEnd); } } } }; LineNode.prototype.charOffsetToLineNumberAndPos = function (lineNumber, charOffset) { var childInfo = this.childFromCharOffset(lineNumber, charOffset); if (!childInfo.child) { return { line: lineNumber, offset: charOffset, }; } else if (childInfo.childIndex < this.children.length) { if (childInfo.child.isLeaf()) { return { line: childInfo.lineNumber, offset: childInfo.charOffset, text: (childInfo.child).text, leaf: (childInfo.child) }; } else { var lineNode = (childInfo.child); return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset); } } else { var lineInfo = this.lineNumberToInfo(this.lineCount(), 0); return { line: this.lineCount(), offset: lineInfo.leaf.charCount() }; } }; LineNode.prototype.lineNumberToInfo = function (lineNumber, charOffset) { var childInfo = this.childFromLineNumber(lineNumber, charOffset); if (!childInfo.child) { return { line: lineNumber, offset: charOffset }; } else if (childInfo.child.isLeaf()) { return { line: lineNumber, offset: childInfo.charOffset, text: (childInfo.child).text, leaf: (childInfo.child) }; } else { var lineNode = (childInfo.child); return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset); } }; LineNode.prototype.childFromLineNumber = function (lineNumber, charOffset) { var child; var relativeLineNumber = lineNumber; var i; var len; for (i = 0, len = this.children.length; i < len; i++) { child = this.children[i]; var childLineCount = child.lineCount(); if (childLineCount >= relativeLineNumber) { break; } else { relativeLineNumber -= childLineCount; charOffset += child.charCount(); } } return { child: child, childIndex: i, relativeLineNumber: relativeLineNumber, charOffset: charOffset }; }; LineNode.prototype.childFromCharOffset = function (lineNumber, charOffset) { var child; var i; var len; for (i = 0, len = this.children.length; i < len; i++) { child = this.children[i]; if (child.charCount() > charOffset) { break; } else { charOffset -= child.charCount(); lineNumber += child.lineCount(); } } return { child: child, childIndex: i, charOffset: charOffset, lineNumber: lineNumber }; }; LineNode.prototype.splitAfter = function (childIndex) { var splitNode; var clen = this.children.length; childIndex++; var endLength = childIndex; if (childIndex < clen) { splitNode = new LineNode(); while (childIndex < clen) { splitNode.add(this.children[childIndex++]); } splitNode.updateCounts(); } this.children.length = endLength; return splitNode; }; LineNode.prototype.remove = function (child) { var childIndex = this.findChildIndex(child); var clen = this.children.length; if (childIndex < (clen - 1)) { for (var i = childIndex; i < (clen - 1); i++) { this.children[i] = this.children[i + 1]; } } this.children.length--; }; LineNode.prototype.findChildIndex = function (child) { var childIndex = 0; var clen = this.children.length; while ((this.children[childIndex] !== child) && (childIndex < clen)) childIndex++; return childIndex; }; LineNode.prototype.insertAt = function (child, nodes) { var childIndex = this.findChildIndex(child); var clen = this.children.length; var nodeCount = nodes.length; // if child is last and there is more room and only one node to place, place it if ((clen < lineCollectionCapacity) && (childIndex === (clen - 1)) && (nodeCount === 1)) { this.add(nodes[0]); this.updateCounts(); return []; } else { var shiftNode = this.splitAfter(childIndex); var nodeIndex = 0; childIndex++; while ((childIndex < lineCollectionCapacity) && (nodeIndex < nodeCount)) { this.children[childIndex++] = nodes[nodeIndex++]; } var splitNodes = []; var splitNodeCount = 0; if (nodeIndex < nodeCount) { splitNodeCount = Math.ceil((nodeCount - nodeIndex) / lineCollectionCapacity); splitNodes = new Array(splitNodeCount); var splitNodeIndex = 0; for (var i = 0; i < splitNodeCount; i++) { splitNodes[i] = new LineNode(); } var splitNode = splitNodes[0]; while (nodeIndex < nodeCount) { splitNode.add(nodes[nodeIndex++]); if (splitNode.children.length === lineCollectionCapacity) { splitNodeIndex++; splitNode = splitNodes[splitNodeIndex]; } } for (var i = splitNodes.length - 1; i >= 0; i--) { if (splitNodes[i].children.length === 0) { splitNodes.length--; } } } if (shiftNode) { splitNodes[splitNodes.length] = shiftNode; } this.updateCounts(); for (var i = 0; i < splitNodeCount; i++) { splitNodes[i].updateCounts(); } return splitNodes; } }; // assume there is room for the item; return true if more room LineNode.prototype.add = function (collection) { this.children[this.children.length] = collection; return (this.children.length < lineCollectionCapacity); }; LineNode.prototype.charCount = function () { return this.totalChars; }; LineNode.prototype.lineCount = function () { return this.totalLines; }; return LineNode; }()); exports.LineNode = LineNode; var LineLeaf = /** @class */ (function () { function LineLeaf(text) { this.text = text; } LineLeaf.prototype.setUdata = function (data) { this.udata = data; }; LineLeaf.prototype.getUdata = function () { return this.udata; }; LineLeaf.prototype.isLeaf = function () { return true; }; LineLeaf.prototype.walk = function (rangeStart, rangeLength, walkFns) { walkFns.leaf(rangeStart, rangeLength, this); }; LineLeaf.prototype.charCount = function () { return this.text.length; }; LineLeaf.prototype.lineCount = function () { return 1; }; return LineLeaf; }()); exports.LineLeaf = LineLeaf; var CharRangeSection; (function (CharRangeSection) { CharRangeSection[CharRangeSection["PreStart"] = 0] = "PreStart"; CharRangeSection[CharRangeSection["Start"] = 1] = "Start"; CharRangeSection[CharRangeSection["Entire"] = 2] = "Entire"; CharRangeSection[CharRangeSection["Mid"] = 3] = "Mid"; CharRangeSection[CharRangeSection["End"] = 4] = "End"; CharRangeSection[CharRangeSection["PostEnd"] = 5] = "PostEnd"; })(CharRangeSection = exports.CharRangeSection || (exports.CharRangeSection = {})); var BaseLineIndexWalker = /** @class */ (function () { function BaseLineIndexWalker() { this.goSubtree = true; this.done = false; } BaseLineIndexWalker.prototype.leaf = function (rangeStart, rangeLength, ll) { }; return BaseLineIndexWalker; }()); var EditWalker = /** @class */ (function (_super) { __extends(EditWalker, _super); function EditWalker() { var _this = _super.call(this) || this; _this.lineIndex = new LineIndex(); _this.endBranch = []; _this.state = CharRangeSection.Entire; _this.initialText = ""; _this.trailingText = ""; _this.suppressTrailingText = false; _this.lineIndex.root = new LineNode(); _this.startPath = [_this.lineIndex.root]; _this.stack = [_this.lineIndex.root]; return _this; } EditWalker.prototype.insertLines = function (insertedText) { if (this.suppressTrailingText) { this.trailingText = ""; } if (insertedText) { insertedText = this.initialText + insertedText + this.trailingText; } else { insertedText = this.initialText + this.trailingText; } var lm = LineIndex.linesFromText(insertedText); var lines = lm.lines; if (lines.length > 1) { if (lines[lines.length - 1] == "") { lines.length--; } } var branchParent; var lastZeroCount; for (var k = this.endBranch.length - 1; k >= 0; k--) { this.endBranch[k].updateCounts(); if (this.endBranch[k].charCount() === 0) { lastZeroCount = this.endBranch[k]; if (k > 0) { branchParent = this.endBranch[k - 1]; } else { branchParent = this.branchNode; } } } if (lastZeroCount) { branchParent.remove(lastZeroCount); } // path at least length two (root and leaf) var insertionNode = this.startPath[this.startPath.length - 2]; var leafNode = this.startPath[this.startPath.length - 1]; var len = lines.length; if (len > 0) { leafNode.text = lines[0]; if (len > 1) { var insertedNodes = new Array(len - 1); var startNode = leafNode; for (var i = 1, len_1 = lines.length; i < len_1; i++) { insertedNodes[i - 1] = new LineLeaf(lines[i]); } var pathIndex = this.startPath.length - 2; while (pathIndex >= 0) { insertionNode = this.startPath[pathIndex]; insertedNodes = insertionNode.insertAt(startNode, insertedNodes); pathIndex--; startNode = insertionNode; } var insertedNodesLen = insertedNodes.length; while (insertedNodesLen > 0) { var newRoot = new LineNode(); newRoot.add(this.lineIndex.root); insertedNodes = newRoot.insertAt(this.lineIndex.root, insertedNodes); insertedNodesLen = insertedNodes.length; this.lineIndex.root = newRoot; } this.lineIndex.root.updateCounts(); } else { for (var j = this.startPath.length - 2; j >= 0; j--) { this.startPath[j].updateCounts(); } } } else { // no content for leaf node, so delete it insertionNode.remove(leafNode); for (var j = this.startPath.length - 2; j >= 0; j--) { this.startPath[j].updateCounts(); } } return this.lineIndex; }; EditWalker.prototype.post = function (relativeStart, relativeLength, lineCollection, parent, nodeType) { // have visited the path for start of range, now looking for end // if range is on single line, we will never make this state transition if (lineCollection === this.lineCollectionAtBranch) { this.state = CharRangeSection.End; } // always pop stack because post only called when child has been visited this.stack.length--; return undefined; }; EditWalker.prototype.pre = function (relativeStart, relativeLength, lineCollection, parent, nodeType) { // currentNode corresponds to parent, but in the new tree var currentNode = this.stack[this.stack.length - 1]; if ((this.state === CharRangeSection.Entire) && (nodeType === CharRangeSection.Start)) { // if range is on single line, we will never make this state transition this.state = CharRangeSection.Start; this.branchNode = currentNode; this.lineCollectionAtBranch = lineCollection; } var child; function fresh(node) { if (node.isLeaf()) { return new LineLeaf(""); } else return new LineNode(); } switch (nodeType) { case CharRangeSection.PreStart: this.goSubtree = false; if (this.state !== CharRangeSection.End) { currentNode.add(lineCollection); } break; case CharRangeSection.Start: if (this.state === CharRangeSection.End) { this.goSubtree = false; } else { child = fresh(lineCollection); currentNode.add(child); this.startPath[this.startPath.length] = child; } break; case CharRangeSection.Entire: if (this.state !== CharRangeSection.End) { child = fresh(lineCollection); currentNode.add(child); this.startPath[this.startPath.length] = child; } else { if (!lineCollection.isLeaf()) { child = fresh(lineCollection); currentNode.add(child); this.endBranch[this.endBranch.length] = child; } } break; case CharRangeSection.Mid: this.goSubtree = false; break; case CharRangeSection.End: if (this.state !== CharRangeSection.End) { this.goSubtree = false; } else { if (!lineCollection.isLeaf()) { child = fresh(lineCollection); currentNode.add(child); this.endBranch[this.endBranch.length] = child; } } break; case CharRangeSection.PostEnd: this.goSubtree = false; if (this.state !== CharRangeSection.Start) { currentNode.add(lineCollection); } break; } if (this.goSubtree) { this.stack[this.stack.length] = child; } return lineCollection; }; // just gather text from the leaves EditWalker.prototype.leaf = function (relativeStart, relativeLength, ll) { if (this.state === CharRangeSection.Start) { this.initialText = ll.text.substring(0, relativeStart); } else if (this.state === CharRangeSection.Entire) { this.initialText = ll.text.substring(0, relativeStart); this.trailingText = ll.text.substring(relativeStart + relativeLength); } else { // state is CharRangeSection.End this.trailingText = ll.text.substring(relativeStart + relativeLength); } }; return EditWalker; }(BaseLineIndexWalker));