alm
Version:
The best IDE for TypeScript
711 lines (710 loc) • 27.3 kB
JavaScript
"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));