lines.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. "use strict";
  2. var __assign = (this && this.__assign) || function () {
  3. __assign = Object.assign || function(t) {
  4. for (var s, i = 1, n = arguments.length; i < n; i++) {
  5. s = arguments[i];
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  7. t[p] = s[p];
  8. }
  9. return t;
  10. };
  11. return __assign.apply(this, arguments);
  12. };
  13. var __importDefault = (this && this.__importDefault) || function (mod) {
  14. return (mod && mod.__esModule) ? mod : { "default": mod };
  15. };
  16. Object.defineProperty(exports, "__esModule", { value: true });
  17. var assert_1 = __importDefault(require("assert"));
  18. var source_map_1 = __importDefault(require("source-map"));
  19. var options_1 = require("./options");
  20. var util_1 = require("./util");
  21. var mapping_1 = __importDefault(require("./mapping"));
  22. var Lines = /** @class */ (function () {
  23. function Lines(infos, sourceFileName) {
  24. if (sourceFileName === void 0) { sourceFileName = null; }
  25. this.infos = infos;
  26. this.mappings = [];
  27. this.cachedSourceMap = null;
  28. this.cachedTabWidth = void 0;
  29. assert_1.default.ok(infos.length > 0);
  30. this.length = infos.length;
  31. this.name = sourceFileName || null;
  32. if (this.name) {
  33. this.mappings.push(new mapping_1.default(this, {
  34. start: this.firstPos(),
  35. end: this.lastPos(),
  36. }));
  37. }
  38. }
  39. Lines.prototype.toString = function (options) {
  40. return this.sliceString(this.firstPos(), this.lastPos(), options);
  41. };
  42. Lines.prototype.getSourceMap = function (sourceMapName, sourceRoot) {
  43. if (!sourceMapName) {
  44. // Although we could make up a name or generate an anonymous
  45. // source map, instead we assume that any consumer who does not
  46. // provide a name does not actually want a source map.
  47. return null;
  48. }
  49. var targetLines = this;
  50. function updateJSON(json) {
  51. json = json || {};
  52. json.file = sourceMapName;
  53. if (sourceRoot) {
  54. json.sourceRoot = sourceRoot;
  55. }
  56. return json;
  57. }
  58. if (targetLines.cachedSourceMap) {
  59. // Since Lines objects are immutable, we can reuse any source map
  60. // that was previously generated. Nevertheless, we return a new
  61. // JSON object here to protect the cached source map from outside
  62. // modification.
  63. return updateJSON(targetLines.cachedSourceMap.toJSON());
  64. }
  65. var smg = new source_map_1.default.SourceMapGenerator(updateJSON());
  66. var sourcesToContents = {};
  67. targetLines.mappings.forEach(function (mapping) {
  68. var sourceCursor = mapping.sourceLines.skipSpaces(mapping.sourceLoc.start) || mapping.sourceLines.lastPos();
  69. var targetCursor = targetLines.skipSpaces(mapping.targetLoc.start) || targetLines.lastPos();
  70. while (util_1.comparePos(sourceCursor, mapping.sourceLoc.end) < 0 &&
  71. util_1.comparePos(targetCursor, mapping.targetLoc.end) < 0) {
  72. var sourceChar = mapping.sourceLines.charAt(sourceCursor);
  73. var targetChar = targetLines.charAt(targetCursor);
  74. assert_1.default.strictEqual(sourceChar, targetChar);
  75. var sourceName = mapping.sourceLines.name;
  76. // Add mappings one character at a time for maximum resolution.
  77. smg.addMapping({
  78. source: sourceName,
  79. original: { line: sourceCursor.line,
  80. column: sourceCursor.column },
  81. generated: { line: targetCursor.line,
  82. column: targetCursor.column }
  83. });
  84. if (!hasOwn.call(sourcesToContents, sourceName)) {
  85. var sourceContent = mapping.sourceLines.toString();
  86. smg.setSourceContent(sourceName, sourceContent);
  87. sourcesToContents[sourceName] = sourceContent;
  88. }
  89. targetLines.nextPos(targetCursor, true);
  90. mapping.sourceLines.nextPos(sourceCursor, true);
  91. }
  92. });
  93. targetLines.cachedSourceMap = smg;
  94. return smg.toJSON();
  95. };
  96. Lines.prototype.bootstrapCharAt = function (pos) {
  97. assert_1.default.strictEqual(typeof pos, "object");
  98. assert_1.default.strictEqual(typeof pos.line, "number");
  99. assert_1.default.strictEqual(typeof pos.column, "number");
  100. var line = pos.line, column = pos.column, strings = this.toString().split(lineTerminatorSeqExp), string = strings[line - 1];
  101. if (typeof string === "undefined")
  102. return "";
  103. if (column === string.length &&
  104. line < strings.length)
  105. return "\n";
  106. if (column >= string.length)
  107. return "";
  108. return string.charAt(column);
  109. };
  110. Lines.prototype.charAt = function (pos) {
  111. assert_1.default.strictEqual(typeof pos, "object");
  112. assert_1.default.strictEqual(typeof pos.line, "number");
  113. assert_1.default.strictEqual(typeof pos.column, "number");
  114. var line = pos.line, column = pos.column, secret = this, infos = secret.infos, info = infos[line - 1], c = column;
  115. if (typeof info === "undefined" || c < 0)
  116. return "";
  117. var indent = this.getIndentAt(line);
  118. if (c < indent)
  119. return " ";
  120. c += info.sliceStart - indent;
  121. if (c === info.sliceEnd &&
  122. line < this.length)
  123. return "\n";
  124. if (c >= info.sliceEnd)
  125. return "";
  126. return info.line.charAt(c);
  127. };
  128. Lines.prototype.stripMargin = function (width, skipFirstLine) {
  129. if (width === 0)
  130. return this;
  131. assert_1.default.ok(width > 0, "negative margin: " + width);
  132. if (skipFirstLine && this.length === 1)
  133. return this;
  134. var lines = new Lines(this.infos.map(function (info, i) {
  135. if (info.line && (i > 0 || !skipFirstLine)) {
  136. info = __assign(__assign({}, info), { indent: Math.max(0, info.indent - width) });
  137. }
  138. return info;
  139. }));
  140. if (this.mappings.length > 0) {
  141. var newMappings_1 = lines.mappings;
  142. assert_1.default.strictEqual(newMappings_1.length, 0);
  143. this.mappings.forEach(function (mapping) {
  144. newMappings_1.push(mapping.indent(width, skipFirstLine, true));
  145. });
  146. }
  147. return lines;
  148. };
  149. Lines.prototype.indent = function (by) {
  150. if (by === 0) {
  151. return this;
  152. }
  153. var lines = new Lines(this.infos.map(function (info) {
  154. if (info.line && !info.locked) {
  155. info = __assign(__assign({}, info), { indent: info.indent + by });
  156. }
  157. return info;
  158. }));
  159. if (this.mappings.length > 0) {
  160. var newMappings_2 = lines.mappings;
  161. assert_1.default.strictEqual(newMappings_2.length, 0);
  162. this.mappings.forEach(function (mapping) {
  163. newMappings_2.push(mapping.indent(by));
  164. });
  165. }
  166. return lines;
  167. };
  168. Lines.prototype.indentTail = function (by) {
  169. if (by === 0) {
  170. return this;
  171. }
  172. if (this.length < 2) {
  173. return this;
  174. }
  175. var lines = new Lines(this.infos.map(function (info, i) {
  176. if (i > 0 && info.line && !info.locked) {
  177. info = __assign(__assign({}, info), { indent: info.indent + by });
  178. }
  179. return info;
  180. }));
  181. if (this.mappings.length > 0) {
  182. var newMappings_3 = lines.mappings;
  183. assert_1.default.strictEqual(newMappings_3.length, 0);
  184. this.mappings.forEach(function (mapping) {
  185. newMappings_3.push(mapping.indent(by, true));
  186. });
  187. }
  188. return lines;
  189. };
  190. Lines.prototype.lockIndentTail = function () {
  191. if (this.length < 2) {
  192. return this;
  193. }
  194. return new Lines(this.infos.map(function (info, i) { return (__assign(__assign({}, info), { locked: i > 0 })); }));
  195. };
  196. Lines.prototype.getIndentAt = function (line) {
  197. assert_1.default.ok(line >= 1, "no line " + line + " (line numbers start from 1)");
  198. return Math.max(this.infos[line - 1].indent, 0);
  199. };
  200. Lines.prototype.guessTabWidth = function () {
  201. if (typeof this.cachedTabWidth === "number") {
  202. return this.cachedTabWidth;
  203. }
  204. var counts = []; // Sparse array.
  205. var lastIndent = 0;
  206. for (var line = 1, last = this.length; line <= last; ++line) {
  207. var info = this.infos[line - 1];
  208. var sliced = info.line.slice(info.sliceStart, info.sliceEnd);
  209. // Whitespace-only lines don't tell us much about the likely tab
  210. // width of this code.
  211. if (isOnlyWhitespace(sliced)) {
  212. continue;
  213. }
  214. var diff = Math.abs(info.indent - lastIndent);
  215. counts[diff] = ~~counts[diff] + 1;
  216. lastIndent = info.indent;
  217. }
  218. var maxCount = -1;
  219. var result = 2;
  220. for (var tabWidth = 1; tabWidth < counts.length; tabWidth += 1) {
  221. if (hasOwn.call(counts, tabWidth) &&
  222. counts[tabWidth] > maxCount) {
  223. maxCount = counts[tabWidth];
  224. result = tabWidth;
  225. }
  226. }
  227. return this.cachedTabWidth = result;
  228. };
  229. // Determine if the list of lines has a first line that starts with a //
  230. // or /* comment. If this is the case, the code may need to be wrapped in
  231. // parens to avoid ASI issues.
  232. Lines.prototype.startsWithComment = function () {
  233. if (this.infos.length === 0) {
  234. return false;
  235. }
  236. var firstLineInfo = this.infos[0], sliceStart = firstLineInfo.sliceStart, sliceEnd = firstLineInfo.sliceEnd, firstLine = firstLineInfo.line.slice(sliceStart, sliceEnd).trim();
  237. return firstLine.length === 0 ||
  238. firstLine.slice(0, 2) === "//" ||
  239. firstLine.slice(0, 2) === "/*";
  240. };
  241. Lines.prototype.isOnlyWhitespace = function () {
  242. return isOnlyWhitespace(this.toString());
  243. };
  244. Lines.prototype.isPrecededOnlyByWhitespace = function (pos) {
  245. var info = this.infos[pos.line - 1];
  246. var indent = Math.max(info.indent, 0);
  247. var diff = pos.column - indent;
  248. if (diff <= 0) {
  249. // If pos.column does not exceed the indentation amount, then
  250. // there must be only whitespace before it.
  251. return true;
  252. }
  253. var start = info.sliceStart;
  254. var end = Math.min(start + diff, info.sliceEnd);
  255. var prefix = info.line.slice(start, end);
  256. return isOnlyWhitespace(prefix);
  257. };
  258. Lines.prototype.getLineLength = function (line) {
  259. var info = this.infos[line - 1];
  260. return this.getIndentAt(line) + info.sliceEnd - info.sliceStart;
  261. };
  262. Lines.prototype.nextPos = function (pos, skipSpaces) {
  263. if (skipSpaces === void 0) { skipSpaces = false; }
  264. var l = Math.max(pos.line, 0), c = Math.max(pos.column, 0);
  265. if (c < this.getLineLength(l)) {
  266. pos.column += 1;
  267. return skipSpaces
  268. ? !!this.skipSpaces(pos, false, true)
  269. : true;
  270. }
  271. if (l < this.length) {
  272. pos.line += 1;
  273. pos.column = 0;
  274. return skipSpaces
  275. ? !!this.skipSpaces(pos, false, true)
  276. : true;
  277. }
  278. return false;
  279. };
  280. Lines.prototype.prevPos = function (pos, skipSpaces) {
  281. if (skipSpaces === void 0) { skipSpaces = false; }
  282. var l = pos.line, c = pos.column;
  283. if (c < 1) {
  284. l -= 1;
  285. if (l < 1)
  286. return false;
  287. c = this.getLineLength(l);
  288. }
  289. else {
  290. c = Math.min(c - 1, this.getLineLength(l));
  291. }
  292. pos.line = l;
  293. pos.column = c;
  294. return skipSpaces
  295. ? !!this.skipSpaces(pos, true, true)
  296. : true;
  297. };
  298. Lines.prototype.firstPos = function () {
  299. // Trivial, but provided for completeness.
  300. return { line: 1, column: 0 };
  301. };
  302. Lines.prototype.lastPos = function () {
  303. return {
  304. line: this.length,
  305. column: this.getLineLength(this.length)
  306. };
  307. };
  308. Lines.prototype.skipSpaces = function (pos, backward, modifyInPlace) {
  309. if (backward === void 0) { backward = false; }
  310. if (modifyInPlace === void 0) { modifyInPlace = false; }
  311. if (pos) {
  312. pos = modifyInPlace ? pos : {
  313. line: pos.line,
  314. column: pos.column
  315. };
  316. }
  317. else if (backward) {
  318. pos = this.lastPos();
  319. }
  320. else {
  321. pos = this.firstPos();
  322. }
  323. if (backward) {
  324. while (this.prevPos(pos)) {
  325. if (!isOnlyWhitespace(this.charAt(pos)) &&
  326. this.nextPos(pos)) {
  327. return pos;
  328. }
  329. }
  330. return null;
  331. }
  332. else {
  333. while (isOnlyWhitespace(this.charAt(pos))) {
  334. if (!this.nextPos(pos)) {
  335. return null;
  336. }
  337. }
  338. return pos;
  339. }
  340. };
  341. Lines.prototype.trimLeft = function () {
  342. var pos = this.skipSpaces(this.firstPos(), false, true);
  343. return pos ? this.slice(pos) : emptyLines;
  344. };
  345. Lines.prototype.trimRight = function () {
  346. var pos = this.skipSpaces(this.lastPos(), true, true);
  347. return pos ? this.slice(this.firstPos(), pos) : emptyLines;
  348. };
  349. Lines.prototype.trim = function () {
  350. var start = this.skipSpaces(this.firstPos(), false, true);
  351. if (start === null) {
  352. return emptyLines;
  353. }
  354. var end = this.skipSpaces(this.lastPos(), true, true);
  355. if (end === null) {
  356. return emptyLines;
  357. }
  358. return this.slice(start, end);
  359. };
  360. Lines.prototype.eachPos = function (callback, startPos, skipSpaces) {
  361. if (startPos === void 0) { startPos = this.firstPos(); }
  362. if (skipSpaces === void 0) { skipSpaces = false; }
  363. var pos = this.firstPos();
  364. if (startPos) {
  365. pos.line = startPos.line,
  366. pos.column = startPos.column;
  367. }
  368. if (skipSpaces && !this.skipSpaces(pos, false, true)) {
  369. return; // Encountered nothing but spaces.
  370. }
  371. do
  372. callback.call(this, pos);
  373. while (this.nextPos(pos, skipSpaces));
  374. };
  375. Lines.prototype.bootstrapSlice = function (start, end) {
  376. var strings = this.toString().split(lineTerminatorSeqExp).slice(start.line - 1, end.line);
  377. if (strings.length > 0) {
  378. strings.push(strings.pop().slice(0, end.column));
  379. strings[0] = strings[0].slice(start.column);
  380. }
  381. return fromString(strings.join("\n"));
  382. };
  383. Lines.prototype.slice = function (start, end) {
  384. if (!end) {
  385. if (!start) {
  386. // The client seems to want a copy of this Lines object, but
  387. // Lines objects are immutable, so it's perfectly adequate to
  388. // return the same object.
  389. return this;
  390. }
  391. // Slice to the end if no end position was provided.
  392. end = this.lastPos();
  393. }
  394. if (!start) {
  395. throw new Error("cannot slice with end but not start");
  396. }
  397. var sliced = this.infos.slice(start.line - 1, end.line);
  398. if (start.line === end.line) {
  399. sliced[0] = sliceInfo(sliced[0], start.column, end.column);
  400. }
  401. else {
  402. assert_1.default.ok(start.line < end.line);
  403. sliced[0] = sliceInfo(sliced[0], start.column);
  404. sliced.push(sliceInfo(sliced.pop(), 0, end.column));
  405. }
  406. var lines = new Lines(sliced);
  407. if (this.mappings.length > 0) {
  408. var newMappings_4 = lines.mappings;
  409. assert_1.default.strictEqual(newMappings_4.length, 0);
  410. this.mappings.forEach(function (mapping) {
  411. var sliced = mapping.slice(this, start, end);
  412. if (sliced) {
  413. newMappings_4.push(sliced);
  414. }
  415. }, this);
  416. }
  417. return lines;
  418. };
  419. Lines.prototype.bootstrapSliceString = function (start, end, options) {
  420. return this.slice(start, end).toString(options);
  421. };
  422. Lines.prototype.sliceString = function (start, end, options) {
  423. if (start === void 0) { start = this.firstPos(); }
  424. if (end === void 0) { end = this.lastPos(); }
  425. options = options_1.normalize(options);
  426. var parts = [];
  427. var _a = options.tabWidth, tabWidth = _a === void 0 ? 2 : _a;
  428. for (var line = start.line; line <= end.line; ++line) {
  429. var info = this.infos[line - 1];
  430. if (line === start.line) {
  431. if (line === end.line) {
  432. info = sliceInfo(info, start.column, end.column);
  433. }
  434. else {
  435. info = sliceInfo(info, start.column);
  436. }
  437. }
  438. else if (line === end.line) {
  439. info = sliceInfo(info, 0, end.column);
  440. }
  441. var indent = Math.max(info.indent, 0);
  442. var before_1 = info.line.slice(0, info.sliceStart);
  443. if (options.reuseWhitespace &&
  444. isOnlyWhitespace(before_1) &&
  445. countSpaces(before_1, options.tabWidth) === indent) {
  446. // Reuse original spaces if the indentation is correct.
  447. parts.push(info.line.slice(0, info.sliceEnd));
  448. continue;
  449. }
  450. var tabs = 0;
  451. var spaces = indent;
  452. if (options.useTabs) {
  453. tabs = Math.floor(indent / tabWidth);
  454. spaces -= tabs * tabWidth;
  455. }
  456. var result = "";
  457. if (tabs > 0) {
  458. result += new Array(tabs + 1).join("\t");
  459. }
  460. if (spaces > 0) {
  461. result += new Array(spaces + 1).join(" ");
  462. }
  463. result += info.line.slice(info.sliceStart, info.sliceEnd);
  464. parts.push(result);
  465. }
  466. return parts.join(options.lineTerminator);
  467. };
  468. Lines.prototype.isEmpty = function () {
  469. return this.length < 2 && this.getLineLength(1) < 1;
  470. };
  471. Lines.prototype.join = function (elements) {
  472. var separator = this;
  473. var infos = [];
  474. var mappings = [];
  475. var prevInfo;
  476. function appendLines(linesOrNull) {
  477. if (linesOrNull === null) {
  478. return;
  479. }
  480. if (prevInfo) {
  481. var info = linesOrNull.infos[0];
  482. var indent = new Array(info.indent + 1).join(" ");
  483. var prevLine_1 = infos.length;
  484. var prevColumn_1 = Math.max(prevInfo.indent, 0) +
  485. prevInfo.sliceEnd - prevInfo.sliceStart;
  486. prevInfo.line = prevInfo.line.slice(0, prevInfo.sliceEnd) + indent + info.line.slice(info.sliceStart, info.sliceEnd);
  487. // If any part of a line is indentation-locked, the whole line
  488. // will be indentation-locked.
  489. prevInfo.locked = prevInfo.locked || info.locked;
  490. prevInfo.sliceEnd = prevInfo.line.length;
  491. if (linesOrNull.mappings.length > 0) {
  492. linesOrNull.mappings.forEach(function (mapping) {
  493. mappings.push(mapping.add(prevLine_1, prevColumn_1));
  494. });
  495. }
  496. }
  497. else if (linesOrNull.mappings.length > 0) {
  498. mappings.push.apply(mappings, linesOrNull.mappings);
  499. }
  500. linesOrNull.infos.forEach(function (info, i) {
  501. if (!prevInfo || i > 0) {
  502. prevInfo = __assign({}, info);
  503. infos.push(prevInfo);
  504. }
  505. });
  506. }
  507. function appendWithSeparator(linesOrNull, i) {
  508. if (i > 0)
  509. appendLines(separator);
  510. appendLines(linesOrNull);
  511. }
  512. elements.map(function (elem) {
  513. var lines = fromString(elem);
  514. if (lines.isEmpty())
  515. return null;
  516. return lines;
  517. }).forEach(function (linesOrNull, i) {
  518. if (separator.isEmpty()) {
  519. appendLines(linesOrNull);
  520. }
  521. else {
  522. appendWithSeparator(linesOrNull, i);
  523. }
  524. });
  525. if (infos.length < 1)
  526. return emptyLines;
  527. var lines = new Lines(infos);
  528. lines.mappings = mappings;
  529. return lines;
  530. };
  531. Lines.prototype.concat = function () {
  532. var args = [];
  533. for (var _i = 0; _i < arguments.length; _i++) {
  534. args[_i] = arguments[_i];
  535. }
  536. var list = [this];
  537. list.push.apply(list, args);
  538. assert_1.default.strictEqual(list.length, args.length + 1);
  539. return emptyLines.join(list);
  540. };
  541. return Lines;
  542. }());
  543. exports.Lines = Lines;
  544. var fromStringCache = {};
  545. var hasOwn = fromStringCache.hasOwnProperty;
  546. var maxCacheKeyLen = 10;
  547. function countSpaces(spaces, tabWidth) {
  548. var count = 0;
  549. var len = spaces.length;
  550. for (var i = 0; i < len; ++i) {
  551. switch (spaces.charCodeAt(i)) {
  552. case 9: // '\t'
  553. assert_1.default.strictEqual(typeof tabWidth, "number");
  554. assert_1.default.ok(tabWidth > 0);
  555. var next = Math.ceil(count / tabWidth) * tabWidth;
  556. if (next === count) {
  557. count += tabWidth;
  558. }
  559. else {
  560. count = next;
  561. }
  562. break;
  563. case 11: // '\v'
  564. case 12: // '\f'
  565. case 13: // '\r'
  566. case 0xfeff: // zero-width non-breaking space
  567. // These characters contribute nothing to indentation.
  568. break;
  569. case 32: // ' '
  570. default: // Treat all other whitespace like ' '.
  571. count += 1;
  572. break;
  573. }
  574. }
  575. return count;
  576. }
  577. exports.countSpaces = countSpaces;
  578. var leadingSpaceExp = /^\s*/;
  579. // As specified here: http://www.ecma-international.org/ecma-262/6.0/#sec-line-terminators
  580. var lineTerminatorSeqExp = /\u000D\u000A|\u000D(?!\u000A)|\u000A|\u2028|\u2029/;
  581. /**
  582. * @param {Object} options - Options object that configures printing.
  583. */
  584. function fromString(string, options) {
  585. if (string instanceof Lines)
  586. return string;
  587. string += "";
  588. var tabWidth = options && options.tabWidth;
  589. var tabless = string.indexOf("\t") < 0;
  590. var cacheable = !options && tabless && (string.length <= maxCacheKeyLen);
  591. assert_1.default.ok(tabWidth || tabless, "No tab width specified but encountered tabs in string\n" + string);
  592. if (cacheable && hasOwn.call(fromStringCache, string))
  593. return fromStringCache[string];
  594. var lines = new Lines(string.split(lineTerminatorSeqExp).map(function (line) {
  595. // TODO: handle null exec result
  596. var spaces = leadingSpaceExp.exec(line)[0];
  597. return {
  598. line: line,
  599. indent: countSpaces(spaces, tabWidth),
  600. // Boolean indicating whether this line can be reindented.
  601. locked: false,
  602. sliceStart: spaces.length,
  603. sliceEnd: line.length
  604. };
  605. }), options_1.normalize(options).sourceFileName);
  606. if (cacheable)
  607. fromStringCache[string] = lines;
  608. return lines;
  609. }
  610. exports.fromString = fromString;
  611. function isOnlyWhitespace(string) {
  612. return !/\S/.test(string);
  613. }
  614. function sliceInfo(info, startCol, endCol) {
  615. var sliceStart = info.sliceStart;
  616. var sliceEnd = info.sliceEnd;
  617. var indent = Math.max(info.indent, 0);
  618. var lineLength = indent + sliceEnd - sliceStart;
  619. if (typeof endCol === "undefined") {
  620. endCol = lineLength;
  621. }
  622. startCol = Math.max(startCol, 0);
  623. endCol = Math.min(endCol, lineLength);
  624. endCol = Math.max(endCol, startCol);
  625. if (endCol < indent) {
  626. indent = endCol;
  627. sliceEnd = sliceStart;
  628. }
  629. else {
  630. sliceEnd -= lineLength - endCol;
  631. }
  632. lineLength = endCol;
  633. lineLength -= startCol;
  634. if (startCol < indent) {
  635. indent -= startCol;
  636. }
  637. else {
  638. startCol -= indent;
  639. indent = 0;
  640. sliceStart += startCol;
  641. }
  642. assert_1.default.ok(indent >= 0);
  643. assert_1.default.ok(sliceStart <= sliceEnd);
  644. assert_1.default.strictEqual(lineLength, indent + sliceEnd - sliceStart);
  645. if (info.indent === indent &&
  646. info.sliceStart === sliceStart &&
  647. info.sliceEnd === sliceEnd) {
  648. return info;
  649. }
  650. return {
  651. line: info.line,
  652. indent: indent,
  653. // A destructive slice always unlocks indentation.
  654. locked: false,
  655. sliceStart: sliceStart,
  656. sliceEnd: sliceEnd
  657. };
  658. }
  659. function concat(elements) {
  660. return emptyLines.join(elements);
  661. }
  662. exports.concat = concat;
  663. ;
  664. // The emptyLines object needs to be created all the way down here so that
  665. // Lines.prototype will be fully populated.
  666. var emptyLines = fromString("");