This repository has been archived on 2020-11-02. You can view files and clone it, but cannot push or open issues or pull requests.
TripSit_Suite/node_modules/line-column/lib/line-column.js
2020-11-01 22:46:04 +00:00

156 lines
4.3 KiB
JavaScript

/**
* line-column - Convert efficiently index to/from line-column in a string
* @module lineColumn
* @license MIT
*/
"use strict";
var isArray = require("isarray");
var isObject = require("isobject");
var slice = Array.prototype.slice;
module.exports = LineColumnFinder;
/**
* Finder for index and line-column from given string.
*
* You can call this without `new` operator as it returns an instance anyway.
*
* @class
* @param {string} str - A string to be parsed.
* @param {Object|number} [options] - Options.
* This can be an index in the string for shorthand of `lineColumn(str, index)`.
* @param {number} [options.origin=1] - The origin value of line and column.
*/
function LineColumnFinder(str, options) {
if (!(this instanceof LineColumnFinder)) {
if (typeof options === "number") {
return (new LineColumnFinder(str)).fromIndex(options);
}
return new LineColumnFinder(str, options);
}
this.str = str || "";
this.lineToIndex = buildLineToIndex(this.str);
options = options || {};
this.origin = typeof options.origin === "undefined" ? 1 : options.origin;
}
/**
* Find line and column from index in the string.
*
* @param {number} index - Index in the string. (0-origin)
* @return {Object|null}
* Found line number and column number in object `{ line: X, col: Y }`.
* If the given index is out of range, it returns `null`.
*/
LineColumnFinder.prototype.fromIndex = function (index) {
if (index < 0 || index >= this.str.length || isNaN(index)) {
return null;
}
var line = findLowerIndexInRangeArray(index, this.lineToIndex);
return {
line: line + this.origin,
col: index - this.lineToIndex[line] + this.origin
};
}
/**
* Find index from line and column in the string.
*
* @param {number|Object|Array} line - Line number in the string.
* This can be an Object of `{ line: X, col: Y }`, or
* an Array of `[line, col]`.
* @param {number} [column] - Column number in the string.
* This must be omitted or undefined when Object or Array is given
* to the first argument.
* @return {number}
* Found index in the string. (always 0-origin)
* If the given line or column is out of range, it returns `-1`.
*/
LineColumnFinder.prototype.toIndex = function (line, column) {
if (typeof column === "undefined") {
if (isArray(line) && line.length >= 2) {
return this.toIndex(line[0], line[1]);
}
if (isObject(line) && "line" in line && ("col" in line || "column" in line)) {
return this.toIndex(line.line, ("col" in line ? line.col : line.column));
}
return -1;
}
if (isNaN(line) || isNaN(column)) {
return -1;
}
line -= this.origin;
column -= this.origin;
if (line >= 0 && column >= 0 && line < this.lineToIndex.length) {
var lineIndex = this.lineToIndex[line];
var nextIndex = (
line === this.lineToIndex.length - 1
? this.str.length
: this.lineToIndex[line + 1]
);
if (column < nextIndex - lineIndex) {
return lineIndex + column;
}
}
return -1;
}
/**
* Build an array of indexes of each line from a string.
*
* @private
* @param str {string} An input string.
* @return {number[]} Built array of indexes. The key is line number.
*/
function buildLineToIndex(str) {
var lines = str.split("\n"),
lineToIndex = new Array(lines.length),
index = 0;
for (var i = 0, l = lines.length; i < l; i++) {
lineToIndex[i] = index;
index += lines[i].length + /* "\n".length */ 1;
}
return lineToIndex;
}
/**
* Find a lower-bound index of a value in a sorted array of ranges.
*
* Assume `arr = [0, 5, 10, 15, 20]` and
* this returns `1` for `value = 7` (5 <= value < 10),
* and returns `3` for `value = 18` (15 <= value < 20).
*
* @private
* @param arr {number[]} An array of values representing ranges.
* @param value {number} A value to be searched.
* @return {number} Found index. If not found `-1`.
*/
function findLowerIndexInRangeArray(value, arr) {
if (value >= arr[arr.length - 1]) {
return arr.length - 1;
}
var min = 0, max = arr.length - 2, mid;
while (min < max) {
mid = min + ((max - min) >> 1);
if (value < arr[mid]) {
max = mid - 1;
} else if (value >= arr[mid + 1]) {
min = mid + 1;
} else { // value >= arr[mid] && value < arr[mid + 1]
min = mid;
break;
}
}
return min;
}