128 lines
3.3 KiB
JavaScript
128 lines
3.3 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Check if the position is within this function location
|
|
*
|
|
* @param {Object} functionLocation
|
|
* @param {Object} position
|
|
* @returns {Boolean}
|
|
*/
|
|
function containsPosition(functionLocation, position) {
|
|
// Start
|
|
return (
|
|
(functionLocation.start.line < position.line ||
|
|
// If the start line is equal check the columns
|
|
(functionLocation.start.line == position.line &&
|
|
functionLocation.start.column <= position.column)) &&
|
|
// End
|
|
(functionLocation.end.line > position.line ||
|
|
// If the end line is equal check the columns
|
|
(functionLocation.end.line == position.line &&
|
|
functionLocation.end.column >= position.column))
|
|
);
|
|
}
|
|
|
|
function containsLocation(parentLocation, childLocation) {
|
|
return (
|
|
containsPosition(parentLocation, childLocation.start) &&
|
|
containsPosition(parentLocation, childLocation.end)
|
|
);
|
|
}
|
|
|
|
function getInnerLocations(locations, position) {
|
|
// First, find the function which directly contains the specified position (line / column)
|
|
let parentIndex;
|
|
for (let i = locations.length - 1; i >= 0; i--) {
|
|
if (containsPosition(locations[i], position)) {
|
|
parentIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (parentIndex == undefined) {
|
|
return [];
|
|
}
|
|
|
|
const parentLoc = locations[parentIndex];
|
|
|
|
// Then, from the nearest location, loop locations again and put locations into
|
|
// the innerLocations array until we get to a location not enclosed by the nearest location.
|
|
const innerLocations = [];
|
|
for (let i = parentIndex + 1; i < locations.length; i++) {
|
|
const loc = locations[i];
|
|
if (!containsLocation(parentLoc, loc)) {
|
|
break;
|
|
}
|
|
innerLocations.push(loc);
|
|
}
|
|
|
|
return innerLocations;
|
|
}
|
|
|
|
/**
|
|
* Sort based on the start line
|
|
*
|
|
* @param {Array} locations
|
|
* @returns
|
|
*/
|
|
function sortByStart(locations) {
|
|
return locations.sort((a, b) => {
|
|
if (a.startLine < b.startLine) {
|
|
return -1;
|
|
} else if (a.startLine === b.startLine) {
|
|
return b.endLine - a.endLine;
|
|
}
|
|
return 1;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Return a new locations array which excludes
|
|
* items that are completely enclosed by another location in the input locations
|
|
*
|
|
* @param locations Notice! The locations MUST be sorted by `sortByStart`
|
|
* so that we can do linear time complexity operation.
|
|
*/
|
|
function removeOverlapLocations(locations) {
|
|
if (!locations.length) {
|
|
return [];
|
|
}
|
|
const firstParent = locations[0];
|
|
return locations.reduce(deduplicateNode, [firstParent]);
|
|
}
|
|
|
|
function deduplicateNode(nodes, location) {
|
|
const parent = nodes[nodes.length - 1];
|
|
if (!containsLocation(parent, location)) {
|
|
nodes.push(location);
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
function getOutOfScopeLines(outOfScopeLocations) {
|
|
if (!outOfScopeLocations) {
|
|
return new Set();
|
|
}
|
|
|
|
const uniqueLines = new Set();
|
|
for (const location of outOfScopeLocations) {
|
|
for (let i = location.startLine; i < location.endLine; i++) {
|
|
uniqueLines.add(i);
|
|
}
|
|
}
|
|
|
|
return uniqueLines;
|
|
}
|
|
|
|
module.exports = {
|
|
containsPosition,
|
|
containsLocation,
|
|
getInnerLocations,
|
|
removeOverlapLocations,
|
|
getOutOfScopeLines,
|
|
sortByStart,
|
|
};
|