line-counter.js 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. /**
  2. * Tracks newlines during parsing in order to provide an efficient API for
  3. * determining the one-indexed `{ line, col }` position for any offset
  4. * within the input.
  5. */
  6. class LineCounter {
  7. constructor() {
  8. this.lineStarts = [];
  9. /**
  10. * Should be called in ascending order. Otherwise, call
  11. * `lineCounter.lineStarts.sort()` before calling `linePos()`.
  12. */
  13. this.addNewLine = (offset) => this.lineStarts.push(offset);
  14. /**
  15. * Performs a binary search and returns the 1-indexed { line, col }
  16. * position of `offset`. If `line === 0`, `addNewLine` has never been
  17. * called or `offset` is before the first known newline.
  18. */
  19. this.linePos = (offset) => {
  20. let low = 0;
  21. let high = this.lineStarts.length;
  22. while (low < high) {
  23. const mid = (low + high) >> 1; // Math.floor((low + high) / 2)
  24. if (this.lineStarts[mid] < offset)
  25. low = mid + 1;
  26. else
  27. high = mid;
  28. }
  29. if (this.lineStarts[low] === offset)
  30. return { line: low + 1, col: 1 };
  31. if (low === 0)
  32. return { line: 0, col: offset };
  33. const start = this.lineStarts[low - 1];
  34. return { line: low, col: offset - start + 1 };
  35. };
  36. }
  37. }
  38. export { LineCounter };