Home Reference Source

lib/ast/map.js

import debugModule from "debug";
const debug = debugModule("debugger:ast:map");

import IntervalTree from "node-interval-tree";


/**
 * @private
 */
export function getRange(node) {
  // src: "<start>:<length>:<_>"
  // returns [start, end]
  let [start, length] = node.src
    .split(":")
    .slice(0, 2)
    .map( (i) => parseInt(i) );

  return [start, start + length];
}

/**
 * @private
 */
export function rangeNodes(node, pointer = "") {
  if (node instanceof Array) {
    return [].concat(
      ...node.map( (sub, i) => rangeNodes(sub, `${pointer}/${i}`) )
    );
  } else if (node instanceof Object) {
    let results = [];

    if (node.src) {
      results.push({pointer, range: getRange(node)});
    }

    return results.concat(
      ...Object.keys(node).map(
        (key) => rangeNodes(node[key], `${pointer}/${key}`)
      )
    );
  } else {
    return [];
  }
}

/**
 * @private
 */
export function findRange(node, sourceStart, sourceLength) {
  let ranges = rangeNodes(node);
  let tree = new IntervalTree();

  ranges.forEach( ({range, pointer}) => {
    let [start, end] = range;

    tree.insert(start, end, {range, pointer});
  });

  let sourceEnd = sourceStart + sourceLength;

  let overlapping = tree.search(sourceStart, sourceEnd);

  // find nodes that fully contain requested range,
  // return longest pointer
  return overlapping
    .filter( ({range}) => sourceStart >= range[0] && sourceEnd <= range[1] )
    .map( ({pointer}) => pointer )
    .reduce( (a, b) => a.length > b.length ? a : b, "" );
}