| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.concat = exports.fromString = exports.countSpaces = exports.Lines = void 0; |
| var tslib_1 = require("tslib"); |
| var assert_1 = tslib_1.__importDefault(require("assert")); |
| var source_map_1 = tslib_1.__importDefault(require("source-map")); |
| var options_1 = require("./options"); |
| var util_1 = require("./util"); |
| var mapping_1 = tslib_1.__importDefault(require("./mapping")); |
| var Lines = /** @class */ (function () { |
| function Lines(infos, sourceFileName) { |
| if (sourceFileName === void 0) { sourceFileName = null; } |
| this.infos = infos; |
| this.mappings = []; |
| this.cachedSourceMap = null; |
| this.cachedTabWidth = void 0; |
| assert_1.default.ok(infos.length > 0); |
| this.length = infos.length; |
| this.name = sourceFileName || null; |
| if (this.name) { |
| this.mappings.push(new mapping_1.default(this, { |
| start: this.firstPos(), |
| end: this.lastPos(), |
| })); |
| } |
| } |
| Lines.prototype.toString = function (options) { |
| return this.sliceString(this.firstPos(), this.lastPos(), options); |
| }; |
| Lines.prototype.getSourceMap = function (sourceMapName, sourceRoot) { |
| if (!sourceMapName) { |
| // Although we could make up a name or generate an anonymous |
| // source map, instead we assume that any consumer who does not |
| // provide a name does not actually want a source map. |
| return null; |
| } |
| var targetLines = this; |
| function updateJSON(json) { |
| json = json || {}; |
| json.file = sourceMapName; |
| if (sourceRoot) { |
| json.sourceRoot = sourceRoot; |
| } |
| return json; |
| } |
| if (targetLines.cachedSourceMap) { |
| // Since Lines objects are immutable, we can reuse any source map |
| // that was previously generated. Nevertheless, we return a new |
| // JSON object here to protect the cached source map from outside |
| // modification. |
| return updateJSON(targetLines.cachedSourceMap.toJSON()); |
| } |
| var smg = new source_map_1.default.SourceMapGenerator(updateJSON()); |
| var sourcesToContents = {}; |
| targetLines.mappings.forEach(function (mapping) { |
| var sourceCursor = mapping.sourceLines.skipSpaces(mapping.sourceLoc.start) || |
| mapping.sourceLines.lastPos(); |
| var targetCursor = targetLines.skipSpaces(mapping.targetLoc.start) || |
| targetLines.lastPos(); |
| while (util_1.comparePos(sourceCursor, mapping.sourceLoc.end) < 0 && |
| util_1.comparePos(targetCursor, mapping.targetLoc.end) < 0) { |
| var sourceChar = mapping.sourceLines.charAt(sourceCursor); |
| var targetChar = targetLines.charAt(targetCursor); |
| assert_1.default.strictEqual(sourceChar, targetChar); |
| var sourceName = mapping.sourceLines.name; |
| // Add mappings one character at a time for maximum resolution. |
| smg.addMapping({ |
| source: sourceName, |
| original: { line: sourceCursor.line, column: sourceCursor.column }, |
| generated: { line: targetCursor.line, column: targetCursor.column }, |
| }); |
| if (!hasOwn.call(sourcesToContents, sourceName)) { |
| var sourceContent = mapping.sourceLines.toString(); |
| smg.setSourceContent(sourceName, sourceContent); |
| sourcesToContents[sourceName] = sourceContent; |
| } |
| targetLines.nextPos(targetCursor, true); |
| mapping.sourceLines.nextPos(sourceCursor, true); |
| } |
| }); |
| targetLines.cachedSourceMap = smg; |
| return smg.toJSON(); |
| }; |
| Lines.prototype.bootstrapCharAt = function (pos) { |
| assert_1.default.strictEqual(typeof pos, "object"); |
| assert_1.default.strictEqual(typeof pos.line, "number"); |
| assert_1.default.strictEqual(typeof pos.column, "number"); |
| var line = pos.line, column = pos.column, strings = this.toString().split(lineTerminatorSeqExp), string = strings[line - 1]; |
| if (typeof string === "undefined") |
| return ""; |
| if (column === string.length && line < strings.length) |
| return "\n"; |
| if (column >= string.length) |
| return ""; |
| return string.charAt(column); |
| }; |
| Lines.prototype.charAt = function (pos) { |
| assert_1.default.strictEqual(typeof pos, "object"); |
| assert_1.default.strictEqual(typeof pos.line, "number"); |
| assert_1.default.strictEqual(typeof pos.column, "number"); |
| var line = pos.line, column = pos.column, secret = this, infos = secret.infos, info = infos[line - 1], c = column; |
| if (typeof info === "undefined" || c < 0) |
| return ""; |
| var indent = this.getIndentAt(line); |
| if (c < indent) |
| return " "; |
| c += info.sliceStart - indent; |
| if (c === info.sliceEnd && line < this.length) |
| return "\n"; |
| if (c >= info.sliceEnd) |
| return ""; |
| return info.line.charAt(c); |
| }; |
| Lines.prototype.stripMargin = function (width, skipFirstLine) { |
| if (width === 0) |
| return this; |
| assert_1.default.ok(width > 0, "negative margin: " + width); |
| if (skipFirstLine && this.length === 1) |
| return this; |
| var lines = new Lines(this.infos.map(function (info, i) { |
| if (info.line && (i > 0 || !skipFirstLine)) { |
| info = tslib_1.__assign(tslib_1.__assign({}, info), { indent: Math.max(0, info.indent - width) }); |
| } |
| return info; |
| })); |
| if (this.mappings.length > 0) { |
| var newMappings_1 = lines.mappings; |
| assert_1.default.strictEqual(newMappings_1.length, 0); |
| this.mappings.forEach(function (mapping) { |
| newMappings_1.push(mapping.indent(width, skipFirstLine, true)); |
| }); |
| } |
| return lines; |
| }; |
| Lines.prototype.indent = function (by) { |
| if (by === 0) { |
| return this; |
| } |
| var lines = new Lines(this.infos.map(function (info) { |
| if (info.line && !info.locked) { |
| info = tslib_1.__assign(tslib_1.__assign({}, info), { indent: info.indent + by }); |
| } |
| return info; |
| })); |
| if (this.mappings.length > 0) { |
| var newMappings_2 = lines.mappings; |
| assert_1.default.strictEqual(newMappings_2.length, 0); |
| this.mappings.forEach(function (mapping) { |
| newMappings_2.push(mapping.indent(by)); |
| }); |
| } |
| return lines; |
| }; |
| Lines.prototype.indentTail = function (by) { |
| if (by === 0) { |
| return this; |
| } |
| if (this.length < 2) { |
| return this; |
| } |
| var lines = new Lines(this.infos.map(function (info, i) { |
| if (i > 0 && info.line && !info.locked) { |
| info = tslib_1.__assign(tslib_1.__assign({}, info), { indent: info.indent + by }); |
| } |
| return info; |
| })); |
| if (this.mappings.length > 0) { |
| var newMappings_3 = lines.mappings; |
| assert_1.default.strictEqual(newMappings_3.length, 0); |
| this.mappings.forEach(function (mapping) { |
| newMappings_3.push(mapping.indent(by, true)); |
| }); |
| } |
| return lines; |
| }; |
| Lines.prototype.lockIndentTail = function () { |
| if (this.length < 2) { |
| return this; |
| } |
| return new Lines(this.infos.map(function (info, i) { return (tslib_1.__assign(tslib_1.__assign({}, info), { locked: i > 0 })); })); |
| }; |
| Lines.prototype.getIndentAt = function (line) { |
| assert_1.default.ok(line >= 1, "no line " + line + " (line numbers start from 1)"); |
| return Math.max(this.infos[line - 1].indent, 0); |
| }; |
| Lines.prototype.guessTabWidth = function () { |
| if (typeof this.cachedTabWidth === "number") { |
| return this.cachedTabWidth; |
| } |
| var counts = []; // Sparse array. |
| var lastIndent = 0; |
| for (var line = 1, last = this.length; line <= last; ++line) { |
| var info = this.infos[line - 1]; |
| var sliced = info.line.slice(info.sliceStart, info.sliceEnd); |
| // Whitespace-only lines don't tell us much about the likely tab |
| // width of this code. |
| if (isOnlyWhitespace(sliced)) { |
| continue; |
| } |
| var diff = Math.abs(info.indent - lastIndent); |
| counts[diff] = ~~counts[diff] + 1; |
| lastIndent = info.indent; |
| } |
| var maxCount = -1; |
| var result = 2; |
| for (var tabWidth = 1; tabWidth < counts.length; tabWidth += 1) { |
| if (hasOwn.call(counts, tabWidth) && counts[tabWidth] > maxCount) { |
| maxCount = counts[tabWidth]; |
| result = tabWidth; |
| } |
| } |
| return (this.cachedTabWidth = result); |
| }; |
| // Determine if the list of lines has a first line that starts with a // |
| // or /* comment. If this is the case, the code may need to be wrapped in |
| // parens to avoid ASI issues. |
| Lines.prototype.startsWithComment = function () { |
| if (this.infos.length === 0) { |
| return false; |
| } |
| var firstLineInfo = this.infos[0], sliceStart = firstLineInfo.sliceStart, sliceEnd = firstLineInfo.sliceEnd, firstLine = firstLineInfo.line.slice(sliceStart, sliceEnd).trim(); |
| return (firstLine.length === 0 || |
| firstLine.slice(0, 2) === "//" || |
| firstLine.slice(0, 2) === "/*"); |
| }; |
| Lines.prototype.isOnlyWhitespace = function () { |
| return isOnlyWhitespace(this.toString()); |
| }; |
| Lines.prototype.isPrecededOnlyByWhitespace = function (pos) { |
| var info = this.infos[pos.line - 1]; |
| var indent = Math.max(info.indent, 0); |
| var diff = pos.column - indent; |
| if (diff <= 0) { |
| // If pos.column does not exceed the indentation amount, then |
| // there must be only whitespace before it. |
| return true; |
| } |
| var start = info.sliceStart; |
| var end = Math.min(start + diff, info.sliceEnd); |
| var prefix = info.line.slice(start, end); |
| return isOnlyWhitespace(prefix); |
| }; |
| Lines.prototype.getLineLength = function (line) { |
| var info = this.infos[line - 1]; |
| return this.getIndentAt(line) + info.sliceEnd - info.sliceStart; |
| }; |
| Lines.prototype.nextPos = function (pos, skipSpaces) { |
| if (skipSpaces === void 0) { skipSpaces = false; } |
| var l = Math.max(pos.line, 0), c = Math.max(pos.column, 0); |
| if (c < this.getLineLength(l)) { |
| pos.column += 1; |
| return skipSpaces ? !!this.skipSpaces(pos, false, true) : true; |
| } |
| if (l < this.length) { |
| pos.line += 1; |
| pos.column = 0; |
| return skipSpaces ? !!this.skipSpaces(pos, false, true) : true; |
| } |
| return false; |
| }; |
| Lines.prototype.prevPos = function (pos, skipSpaces) { |
| if (skipSpaces === void 0) { skipSpaces = false; } |
| var l = pos.line, c = pos.column; |
| if (c < 1) { |
| l -= 1; |
| if (l < 1) |
| return false; |
| c = this.getLineLength(l); |
| } |
| else { |
| c = Math.min(c - 1, this.getLineLength(l)); |
| } |
| pos.line = l; |
| pos.column = c; |
| return skipSpaces ? !!this.skipSpaces(pos, true, true) : true; |
| }; |
| Lines.prototype.firstPos = function () { |
| // Trivial, but provided for completeness. |
| return { line: 1, column: 0 }; |
| }; |
| Lines.prototype.lastPos = function () { |
| return { |
| line: this.length, |
| column: this.getLineLength(this.length), |
| }; |
| }; |
| Lines.prototype.skipSpaces = function (pos, backward, modifyInPlace) { |
| if (backward === void 0) { backward = false; } |
| if (modifyInPlace === void 0) { modifyInPlace = false; } |
| if (pos) { |
| pos = modifyInPlace |
| ? pos |
| : { |
| line: pos.line, |
| column: pos.column, |
| }; |
| } |
| else if (backward) { |
| pos = this.lastPos(); |
| } |
| else { |
| pos = this.firstPos(); |
| } |
| if (backward) { |
| while (this.prevPos(pos)) { |
| if (!isOnlyWhitespace(this.charAt(pos)) && this.nextPos(pos)) { |
| return pos; |
| } |
| } |
| return null; |
| } |
| else { |
| while (isOnlyWhitespace(this.charAt(pos))) { |
| if (!this.nextPos(pos)) { |
| return null; |
| } |
| } |
| return pos; |
| } |
| }; |
| Lines.prototype.trimLeft = function () { |
| var pos = this.skipSpaces(this.firstPos(), false, true); |
| return pos ? this.slice(pos) : emptyLines; |
| }; |
| Lines.prototype.trimRight = function () { |
| var pos = this.skipSpaces(this.lastPos(), true, true); |
| return pos ? this.slice(this.firstPos(), pos) : emptyLines; |
| }; |
| Lines.prototype.trim = function () { |
| var start = this.skipSpaces(this.firstPos(), false, true); |
| if (start === null) { |
| return emptyLines; |
| } |
| var end = this.skipSpaces(this.lastPos(), true, true); |
| if (end === null) { |
| return emptyLines; |
| } |
| return this.slice(start, end); |
| }; |
| Lines.prototype.eachPos = function (callback, startPos, skipSpaces) { |
| if (startPos === void 0) { startPos = this.firstPos(); } |
| if (skipSpaces === void 0) { skipSpaces = false; } |
| var pos = this.firstPos(); |
| if (startPos) { |
| (pos.line = startPos.line), (pos.column = startPos.column); |
| } |
| if (skipSpaces && !this.skipSpaces(pos, false, true)) { |
| return; // Encountered nothing but spaces. |
| } |
| do |
| callback.call(this, pos); |
| while (this.nextPos(pos, skipSpaces)); |
| }; |
| Lines.prototype.bootstrapSlice = function (start, end) { |
| var strings = this.toString() |
| .split(lineTerminatorSeqExp) |
| .slice(start.line - 1, end.line); |
| if (strings.length > 0) { |
| strings.push(strings.pop().slice(0, end.column)); |
| strings[0] = strings[0].slice(start.column); |
| } |
| return fromString(strings.join("\n")); |
| }; |
| Lines.prototype.slice = function (start, end) { |
| if (!end) { |
| if (!start) { |
| // The client seems to want a copy of this Lines object, but |
| // Lines objects are immutable, so it's perfectly adequate to |
| // return the same object. |
| return this; |
| } |
| // Slice to the end if no end position was provided. |
| end = this.lastPos(); |
| } |
| if (!start) { |
| throw new Error("cannot slice with end but not start"); |
| } |
| var sliced = this.infos.slice(start.line - 1, end.line); |
| if (start.line === end.line) { |
| sliced[0] = sliceInfo(sliced[0], start.column, end.column); |
| } |
| else { |
| assert_1.default.ok(start.line < end.line); |
| sliced[0] = sliceInfo(sliced[0], start.column); |
| sliced.push(sliceInfo(sliced.pop(), 0, end.column)); |
| } |
| var lines = new Lines(sliced); |
| if (this.mappings.length > 0) { |
| var newMappings_4 = lines.mappings; |
| assert_1.default.strictEqual(newMappings_4.length, 0); |
| this.mappings.forEach(function (mapping) { |
| var sliced = mapping.slice(this, start, end); |
| if (sliced) { |
| newMappings_4.push(sliced); |
| } |
| }, this); |
| } |
| return lines; |
| }; |
| Lines.prototype.bootstrapSliceString = function (start, end, options) { |
| return this.slice(start, end).toString(options); |
| }; |
| Lines.prototype.sliceString = function (start, end, options) { |
| if (start === void 0) { start = this.firstPos(); } |
| if (end === void 0) { end = this.lastPos(); } |
| var _a = options_1.normalize(options), tabWidth = _a.tabWidth, useTabs = _a.useTabs, reuseWhitespace = _a.reuseWhitespace, lineTerminator = _a.lineTerminator; |
| var parts = []; |
| for (var line = start.line; line <= end.line; ++line) { |
| var info = this.infos[line - 1]; |
| if (line === start.line) { |
| if (line === end.line) { |
| info = sliceInfo(info, start.column, end.column); |
| } |
| else { |
| info = sliceInfo(info, start.column); |
| } |
| } |
| else if (line === end.line) { |
| info = sliceInfo(info, 0, end.column); |
| } |
| var indent = Math.max(info.indent, 0); |
| var before_1 = info.line.slice(0, info.sliceStart); |
| if (reuseWhitespace && |
| isOnlyWhitespace(before_1) && |
| countSpaces(before_1, tabWidth) === indent) { |
| // Reuse original spaces if the indentation is correct. |
| parts.push(info.line.slice(0, info.sliceEnd)); |
| continue; |
| } |
| var tabs = 0; |
| var spaces = indent; |
| if (useTabs) { |
| tabs = Math.floor(indent / tabWidth); |
| spaces -= tabs * tabWidth; |
| } |
| var result = ""; |
| if (tabs > 0) { |
| result += new Array(tabs + 1).join("\t"); |
| } |
| if (spaces > 0) { |
| result += new Array(spaces + 1).join(" "); |
| } |
| result += info.line.slice(info.sliceStart, info.sliceEnd); |
| parts.push(result); |
| } |
| return parts.join(lineTerminator); |
| }; |
| Lines.prototype.isEmpty = function () { |
| return this.length < 2 && this.getLineLength(1) < 1; |
| }; |
| Lines.prototype.join = function (elements) { |
| var separator = this; |
| var infos = []; |
| var mappings = []; |
| var prevInfo; |
| function appendLines(linesOrNull) { |
| if (linesOrNull === null) { |
| return; |
| } |
| if (prevInfo) { |
| var info = linesOrNull.infos[0]; |
| var indent = new Array(info.indent + 1).join(" "); |
| var prevLine_1 = infos.length; |
| var prevColumn_1 = Math.max(prevInfo.indent, 0) + |
| prevInfo.sliceEnd - |
| prevInfo.sliceStart; |
| prevInfo.line = |
| prevInfo.line.slice(0, prevInfo.sliceEnd) + |
| indent + |
| info.line.slice(info.sliceStart, info.sliceEnd); |
| // If any part of a line is indentation-locked, the whole line |
| // will be indentation-locked. |
| prevInfo.locked = prevInfo.locked || info.locked; |
| prevInfo.sliceEnd = prevInfo.line.length; |
| if (linesOrNull.mappings.length > 0) { |
| linesOrNull.mappings.forEach(function (mapping) { |
| mappings.push(mapping.add(prevLine_1, prevColumn_1)); |
| }); |
| } |
| } |
| else if (linesOrNull.mappings.length > 0) { |
| mappings.push.apply(mappings, linesOrNull.mappings); |
| } |
| linesOrNull.infos.forEach(function (info, i) { |
| if (!prevInfo || i > 0) { |
| prevInfo = tslib_1.__assign({}, info); |
| infos.push(prevInfo); |
| } |
| }); |
| } |
| function appendWithSeparator(linesOrNull, i) { |
| if (i > 0) |
| appendLines(separator); |
| appendLines(linesOrNull); |
| } |
| elements |
| .map(function (elem) { |
| var lines = fromString(elem); |
| if (lines.isEmpty()) |
| return null; |
| return lines; |
| }) |
| .forEach(function (linesOrNull, i) { |
| if (separator.isEmpty()) { |
| appendLines(linesOrNull); |
| } |
| else { |
| appendWithSeparator(linesOrNull, i); |
| } |
| }); |
| if (infos.length < 1) |
| return emptyLines; |
| var lines = new Lines(infos); |
| lines.mappings = mappings; |
| return lines; |
| }; |
| Lines.prototype.concat = function () { |
| var args = []; |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args[_i] = arguments[_i]; |
| } |
| var list = [this]; |
| list.push.apply(list, args); |
| assert_1.default.strictEqual(list.length, args.length + 1); |
| return emptyLines.join(list); |
| }; |
| return Lines; |
| }()); |
| exports.Lines = Lines; |
| var fromStringCache = {}; |
| var hasOwn = fromStringCache.hasOwnProperty; |
| var maxCacheKeyLen = 10; |
| function countSpaces(spaces, tabWidth) { |
| var count = 0; |
| var len = spaces.length; |
| for (var i = 0; i < len; ++i) { |
| switch (spaces.charCodeAt(i)) { |
| case 9: { |
| // '\t' |
| assert_1.default.strictEqual(typeof tabWidth, "number"); |
| assert_1.default.ok(tabWidth > 0); |
| var next = Math.ceil(count / tabWidth) * tabWidth; |
| if (next === count) { |
| count += tabWidth; |
| } |
| else { |
| count = next; |
| } |
| break; |
| } |
| case 11: // '\v' |
| case 12: // '\f' |
| case 13: // '\r' |
| case 0xfeff: // zero-width non-breaking space |
| // These characters contribute nothing to indentation. |
| break; |
| case 32: // ' ' |
| default: |
| // Treat all other whitespace like ' '. |
| count += 1; |
| break; |
| } |
| } |
| return count; |
| } |
| exports.countSpaces = countSpaces; |
| var leadingSpaceExp = /^\s*/; |
| // As specified here: http://www.ecma-international.org/ecma-262/6.0/#sec-line-terminators |
| var lineTerminatorSeqExp = /\u000D\u000A|\u000D(?!\u000A)|\u000A|\u2028|\u2029/; |
| /** |
| * @param {Object} options - Options object that configures printing. |
| */ |
| function fromString(string, options) { |
| if (string instanceof Lines) |
| return string; |
| string += ""; |
| var tabWidth = options && options.tabWidth; |
| var tabless = string.indexOf("\t") < 0; |
| var cacheable = !options && tabless && string.length <= maxCacheKeyLen; |
| assert_1.default.ok(tabWidth || tabless, "No tab width specified but encountered tabs in string\n" + string); |
| if (cacheable && hasOwn.call(fromStringCache, string)) |
| return fromStringCache[string]; |
| var lines = new Lines(string.split(lineTerminatorSeqExp).map(function (line) { |
| // TODO: handle null exec result |
| var spaces = leadingSpaceExp.exec(line)[0]; |
| return { |
| line: line, |
| indent: countSpaces(spaces, tabWidth), |
| // Boolean indicating whether this line can be reindented. |
| locked: false, |
| sliceStart: spaces.length, |
| sliceEnd: line.length, |
| }; |
| }), options_1.normalize(options).sourceFileName); |
| if (cacheable) |
| fromStringCache[string] = lines; |
| return lines; |
| } |
| exports.fromString = fromString; |
| function isOnlyWhitespace(string) { |
| return !/\S/.test(string); |
| } |
| function sliceInfo(info, startCol, endCol) { |
| var sliceStart = info.sliceStart; |
| var sliceEnd = info.sliceEnd; |
| var indent = Math.max(info.indent, 0); |
| var lineLength = indent + sliceEnd - sliceStart; |
| if (typeof endCol === "undefined") { |
| endCol = lineLength; |
| } |
| startCol = Math.max(startCol, 0); |
| endCol = Math.min(endCol, lineLength); |
| endCol = Math.max(endCol, startCol); |
| if (endCol < indent) { |
| indent = endCol; |
| sliceEnd = sliceStart; |
| } |
| else { |
| sliceEnd -= lineLength - endCol; |
| } |
| lineLength = endCol; |
| lineLength -= startCol; |
| if (startCol < indent) { |
| indent -= startCol; |
| } |
| else { |
| startCol -= indent; |
| indent = 0; |
| sliceStart += startCol; |
| } |
| assert_1.default.ok(indent >= 0); |
| assert_1.default.ok(sliceStart <= sliceEnd); |
| assert_1.default.strictEqual(lineLength, indent + sliceEnd - sliceStart); |
| if (info.indent === indent && |
| info.sliceStart === sliceStart && |
| info.sliceEnd === sliceEnd) { |
| return info; |
| } |
| return { |
| line: info.line, |
| indent: indent, |
| // A destructive slice always unlocks indentation. |
| locked: false, |
| sliceStart: sliceStart, |
| sliceEnd: sliceEnd, |
| }; |
| } |
| function concat(elements) { |
| return emptyLines.join(elements); |
| } |
| exports.concat = concat; |
| // The emptyLines object needs to be created all the way down here so that |
| // Lines.prototype will be fully populated. |
| var emptyLines = fromString(""); |