Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

source-utils.js 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.getLinePos = getLinePos;
  6. exports.getLine = getLine;
  7. exports.getPrettyContext = getPrettyContext;
  8. function findLineStarts(src) {
  9. const ls = [0];
  10. let offset = src.indexOf('\n');
  11. while (offset !== -1) {
  12. offset += 1;
  13. ls.push(offset);
  14. offset = src.indexOf('\n', offset);
  15. }
  16. return ls;
  17. }
  18. function getSrcInfo(cst) {
  19. let lineStarts, src;
  20. if (typeof cst === 'string') {
  21. lineStarts = findLineStarts(cst);
  22. src = cst;
  23. } else {
  24. if (Array.isArray(cst)) cst = cst[0];
  25. if (cst && cst.context) {
  26. if (!cst.lineStarts) cst.lineStarts = findLineStarts(cst.context.src);
  27. lineStarts = cst.lineStarts;
  28. src = cst.context.src;
  29. }
  30. }
  31. return {
  32. lineStarts,
  33. src
  34. };
  35. }
  36. /**
  37. * @typedef {Object} LinePos - One-indexed position in the source
  38. * @property {number} line
  39. * @property {number} col
  40. */
  41. /**
  42. * Determine the line/col position matching a character offset.
  43. *
  44. * Accepts a source string or a CST document as the second parameter. With
  45. * the latter, starting indices for lines are cached in the document as
  46. * `lineStarts: number[]`.
  47. *
  48. * Returns a one-indexed `{ line, col }` location if found, or
  49. * `undefined` otherwise.
  50. *
  51. * @param {number} offset
  52. * @param {string|Document|Document[]} cst
  53. * @returns {?LinePos}
  54. */
  55. function getLinePos(offset, cst) {
  56. if (typeof offset !== 'number' || offset < 0) return null;
  57. const {
  58. lineStarts,
  59. src
  60. } = getSrcInfo(cst);
  61. if (!lineStarts || !src || offset > src.length) return null;
  62. for (let i = 0; i < lineStarts.length; ++i) {
  63. const start = lineStarts[i];
  64. if (offset < start) {
  65. return {
  66. line: i,
  67. col: offset - lineStarts[i - 1] + 1
  68. };
  69. }
  70. if (offset === start) return {
  71. line: i + 1,
  72. col: 1
  73. };
  74. }
  75. const line = lineStarts.length;
  76. return {
  77. line,
  78. col: offset - lineStarts[line - 1] + 1
  79. };
  80. }
  81. /**
  82. * Get a specified line from the source.
  83. *
  84. * Accepts a source string or a CST document as the second parameter. With
  85. * the latter, starting indices for lines are cached in the document as
  86. * `lineStarts: number[]`.
  87. *
  88. * Returns the line as a string if found, or `null` otherwise.
  89. *
  90. * @param {number} line One-indexed line number
  91. * @param {string|Document|Document[]} cst
  92. * @returns {?string}
  93. */
  94. function getLine(line, cst) {
  95. const {
  96. lineStarts,
  97. src
  98. } = getSrcInfo(cst);
  99. if (!lineStarts || !(line >= 1) || line > lineStarts.length) return null;
  100. const start = lineStarts[line - 1];
  101. let end = lineStarts[line]; // undefined for last line; that's ok for slice()
  102. while (end && end > start && src[end - 1] === '\n') --end;
  103. return src.slice(start, end);
  104. }
  105. /**
  106. * Pretty-print the starting line from the source indicated by the range `pos`
  107. *
  108. * Trims output to `maxWidth` chars while keeping the starting column visible,
  109. * using `…` at either end to indicate dropped characters.
  110. *
  111. * Returns a two-line string (or `null`) with `\n` as separator; the second line
  112. * will hold appropriately indented `^` marks indicating the column range.
  113. *
  114. * @param {Object} pos
  115. * @param {LinePos} pos.start
  116. * @param {LinePos} [pos.end]
  117. * @param {string|Document|Document[]*} cst
  118. * @param {number} [maxWidth=80]
  119. * @returns {?string}
  120. */
  121. function getPrettyContext({
  122. start,
  123. end
  124. }, cst, maxWidth = 80) {
  125. let src = getLine(start.line, cst);
  126. if (!src) return null;
  127. let {
  128. col
  129. } = start;
  130. if (src.length > maxWidth) {
  131. if (col <= maxWidth - 10) {
  132. src = src.substr(0, maxWidth - 1) + '…';
  133. } else {
  134. const halfWidth = Math.round(maxWidth / 2);
  135. if (src.length > col + halfWidth) src = src.substr(0, col + halfWidth - 1) + '…';
  136. col -= src.length - maxWidth;
  137. src = '…' + src.substr(1 - maxWidth);
  138. }
  139. }
  140. let errLen = 1;
  141. let errEnd = '';
  142. if (end) {
  143. if (end.line === start.line && col + (end.col - start.col) <= maxWidth + 1) {
  144. errLen = end.col - start.col;
  145. } else {
  146. errLen = Math.min(src.length + 1, maxWidth) - col;
  147. errEnd = '…';
  148. }
  149. }
  150. const offset = col > 1 ? ' '.repeat(col - 1) : '';
  151. const err = '^'.repeat(errLen);
  152. return `${src}\n${offset}${err}${errEnd}`;
  153. }