/* 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 . */ // @flow import type { BindingLocationType, BindingType } from "../../../workers/parser"; import { positionCmp } from "./positionCmp"; import { filterSortedArray } from "./filtering"; import { mappingContains } from "./mappingContains"; import type { Source, Location, Position } from "../../../types"; import type { GeneratedBindingLocation } from "./buildGeneratedBindingList"; export type ApplicableBinding = { binding: GeneratedBindingLocation, range: GeneratedRange, firstInRange: boolean, firstOnLine: boolean }; type GeneratedRange = { start: Position, end: Position }; export async function originalRangeStartsInside( source: Source, { start, end }: { start: Location, end: Location }, sourceMaps: any ) { const endPosition = await sourceMaps.getGeneratedLocation(end, source); const startPosition = await sourceMaps.getGeneratedLocation(start, source); // If the start and end positions collapse into eachother, it means that // the range in the original content didn't _start_ at the start position. // Since this likely means that the range doesn't logically apply to this // binding location, we skip it. return positionCmp(startPosition, endPosition) !== 0; } export async function getApplicableBindingsForOriginalPosition( generatedAstBindings: Array, source: Source, { start, end }: { start: Location, end: Location }, bindingType: BindingType, locationType: BindingLocationType, sourceMaps: any ): Promise> { const ranges = await sourceMaps.getGeneratedRanges(start, source); const resultRanges = ranges.map(mapRange => ({ start: { line: mapRange.line, column: mapRange.columnStart }, end: { line: mapRange.line, // SourceMapConsumer's 'lastColumn' is inclusive, so we add 1 to make // it exclusive like all other locations. column: mapRange.columnEnd + 1 } })); // When searching for imports, we expand the range to up to the next available // mapping to allow for import declarations that are composed of multiple // variable statements, where the later ones are entirely unmapped. // Babel 6 produces imports in this style, e.g. // // var _mod = require("mod"); // mapped from import statement // var _mod2 = interop(_mod); // entirely unmapped if (bindingType === "import" && locationType !== "ref") { const endPosition = await sourceMaps.getGeneratedLocation(end, source); const startPosition = await sourceMaps.getGeneratedLocation(start, source); for (const range of resultRanges) { if ( mappingContains(range, { start: startPosition, end: startPosition }) && positionCmp(range.end, endPosition) < 0 ) { range.end.line = endPosition.line; range.end.column = endPosition.column; break; } } } return filterApplicableBindings(generatedAstBindings, resultRanges); } function filterApplicableBindings( bindings: Array, ranges: Array ): Array { const result = []; for (const range of ranges) { // Any binding overlapping a part of the mapping range. const filteredBindings = filterSortedArray(bindings, binding => { if (positionCmp(binding.loc.end, range.start) <= 0) { return -1; } if (positionCmp(binding.loc.start, range.end) >= 0) { return 1; } return 0; }); let firstInRange = true; let firstOnLine = true; let line = -1; for (const binding of filteredBindings) { if (binding.loc.start.line === line) { firstOnLine = false; } else { line = binding.loc.start.line; firstOnLine = true; } result.push({ binding, range, firstOnLine, firstInRange }); firstInRange = false; } } return result; }