1- import { encode , decode , encodeRangeMappings , decodeRangeMappings } from '@jridgewell/sourcemap-codec' ;
1+ import {
2+ encode ,
3+ decode ,
4+ encodeRangeMappings ,
5+ decodeRangeMappings ,
6+ } from '@jridgewell/sourcemap-codec' ;
27
38import resolver from './resolve' ;
49import maybeSort from './sort' ;
@@ -39,6 +44,7 @@ import type {
3944 XInput ,
4045 SectionedSourceMap ,
4146 Ro ,
47+ RangeInfo ,
4248} from './types' ;
4349import type { Source } from './by-source' ;
4450import type { MemoState } from './binary-search' ;
@@ -156,9 +162,9 @@ export class TraceMap implements SourceMap {
156162 declare private _decodedRangeMappings : number [ ] [ ] | undefined ;
157163
158164 /**
159- * A set of segments that are range mappings.
165+ * A map of segments that are range mappings to info about the range .
160166 */
161- declare private _rangeSegments : Set < SourceMapSegment > | undefined ;
167+ declare private _rangeSegments : Map < SourceMapSegment , RangeInfo > | undefined ;
162168
163169 constructor ( map : Ro < SourceMapInput > , mapUrl ?: string | null ) {
164170 const isString = typeof map === 'string' ;
@@ -272,6 +278,77 @@ export function traceSegment(
272278 ) ;
273279}
274280
281+ /**
282+ * Find all the segments that are in a given range. Used to find
283+ * segments that are relevant for composing a range mapping with
284+ * another range.
285+ */
286+ export function traceSegmentsInRange (
287+ map : TraceMap ,
288+ startLine : number ,
289+ startColumn : number ,
290+ endLine : number ,
291+ endColumn : number ,
292+ ) : Readonly < SourceMapSegment > [ ] {
293+ const lines = decodedMappings ( map ) ;
294+ const segments = startLine < lines . length ? lines [ startLine ] : [ ] ;
295+ const memo = cast ( map ) . _decodedMemo ;
296+
297+ if ( startLine >= lines . length || endLine >= lines . length ) return [ ] ;
298+
299+ const segmentsInRange = [ ] ;
300+
301+ let startIndex = memoizedBinarySearchSegments ( segments , startColumn , memo , startLine ) ;
302+ if ( bsFound ) {
303+ startIndex = lowerBound ( segments , startColumn , startIndex ) ;
304+ } else {
305+ startIndex ++ ;
306+ }
307+ if ( startIndex === segments . length ) return [ ] ;
308+
309+ const range = ( start : number , end : number ) => {
310+ return Array . from ( { length : end - start + 1 } , ( _ , i : number ) => i + start ) ;
311+ } ;
312+ const gen = ( line : number ) => ( index : number ) => {
313+ return lines [ line ] [ index ] ;
314+ } ;
315+
316+ function previousPosition ( line : number , column : number ) {
317+ if ( column === 0 ) {
318+ line -- ;
319+ column = Infinity ;
320+ } else {
321+ column -- ;
322+ }
323+
324+ return { line, column } ;
325+ }
326+
327+ if ( startLine == endLine ) {
328+ // We need to use the position decremented by one position because we
329+ // want the index of a true lower bound, not an equal position. That is,
330+ // the end point of the range should be exclusive and not inclusive.
331+ const { line, column } = previousPosition ( endLine , endColumn ) ;
332+ let endIndex = memoizedBinarySearchSegments ( segments , column , memo , line ) ;
333+ if ( bsFound ) {
334+ endIndex = upperBound ( segments , column , endIndex ) ;
335+ }
336+ segmentsInRange . push ( ...range ( startIndex , endIndex ) . map ( gen ( startLine ) ) ) ;
337+ } else {
338+ segmentsInRange . push ( ...range ( startIndex , segments . length - 1 ) . map ( gen ( startLine ) ) ) ;
339+ for ( let i = startLine + 1 ; i < endLine ; i ++ ) {
340+ segmentsInRange . push ( ...range ( 0 , lines [ i ] . length - 1 ) . map ( gen ( i ) ) ) ;
341+ }
342+ let endIndex = memoizedBinarySearchSegments ( lines [ endLine ] , endColumn , memo , endLine ) ;
343+ if ( bsFound ) {
344+ endIndex = upperBound ( lines [ endLine ] , endColumn , endIndex ) ;
345+ }
346+ segmentsInRange . push ( ...range ( 0 , endIndex ) . map ( gen ( endLine ) ) ) ;
347+ }
348+
349+ return segmentsInRange ;
350+ }
351+
275352/**
276353 * A higher-level API to find the source/line/column associated with a generated line/column
277354 * (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in
@@ -397,6 +474,16 @@ export function isIgnored(map: TraceMap, source: string): boolean {
397474 return index === - 1 ? false : ignoreList . includes ( index ) ;
398475}
399476
477+ /**
478+ * Determines if a segment is for a range mapping, and if so returns
479+ * some metadata about the range.
480+ */
481+ export function isRange ( map : TraceMap , segment : Readonly < SourceMapSegment > ) : RangeInfo | false {
482+ const decoded = decodedMappings ( map ) ;
483+ const rangeSegments = initRangeSegments ( map , decoded ) ;
484+ return rangeSegments . get ( segment as SourceMapSegment ) || false ;
485+ }
486+
400487/**
401488 * A helper that skips sorting of the input map's mappings array, which can be expensive for larger
402489 * maps.
@@ -485,23 +572,23 @@ function GMapping(
485572 */
486573function traceSegmentInternal < T extends SourceMapSegment | ReverseSegment > (
487574 lines : readonly T [ ] [ ] ,
488- rangeSegments : Set < T > ,
575+ rangeSegments : Set < T > | Map < T , RangeInfo > ,
489576 memo : MemoState ,
490577 line : number ,
491578 column : number ,
492579 bias : Bias ,
493580) : T | null ;
494581function traceSegmentInternal < T extends SourceMapSegment | ReverseSegment > (
495582 lines : readonly T [ ] [ ] ,
496- rangeSegments : Set < T > ,
583+ rangeSegments : Set < T > | Map < T , RangeInfo > ,
497584 memo : MemoState ,
498585 line : number ,
499586 column : number ,
500587 bias : Bias ,
501588) : T | null ;
502589function traceSegmentInternal < T extends SourceMapSegment | ReverseSegment > (
503590 lines : readonly T [ ] [ ] ,
504- rangeSegments : Set < T > ,
591+ rangeSegments : Set < T > | Map < T , RangeInfo > ,
505592 memo : MemoState ,
506593 line : number ,
507594 column : number ,
@@ -700,7 +787,7 @@ function initRangeSegments(map: TraceMap, decoded: readonly SourceMapSegment[][]
700787 const existing = cast ( map ) . _rangeSegments ;
701788 if ( existing != null ) return existing ;
702789
703- const set = new Set < SourceMapSegment > ( ) ;
790+ const set = new Map < SourceMapSegment , RangeInfo > ( ) ;
704791 cast ( map ) . _rangeSegments = set ;
705792 const rangeMappings = decodedRangeMappings ( map ) ;
706793 if ( rangeMappings == null ) return set ;
@@ -710,13 +797,37 @@ function initRangeSegments(map: TraceMap, decoded: readonly SourceMapSegment[][]
710797 const ranges = rangeMappings [ i ] ;
711798 for ( let j = 0 ; j < ranges . length ; j ++ ) {
712799 const seg = line [ ranges [ j ] ] ;
713- set . add ( seg ) ;
800+ const { line : endLine , index : endIndex } = findNextSegment ( decoded , i , ranges [ j ] ) ;
801+ const endSegment = endLine && endIndex ? decoded [ endLine ] [ endIndex ] : null ;
802+ set . set ( seg , { line : i , endLine, endSegment } ) ;
714803 }
715804 }
716805
717806 return set ;
718807}
719808
809+ /**
810+ * Find the index of the next segment in this line or in subsequent lines.
811+ * Return null if there was no next segment.
812+ */
813+ function findNextSegment < T extends SourceMapSegment > (
814+ lines : readonly T [ ] [ ] ,
815+ line : number ,
816+ index : number ,
817+ ) : { line : number | null ; index : number | null } {
818+ if ( index + 1 < lines [ line ] . length ) {
819+ return { line, index : index + 1 } ;
820+ } else {
821+ for ( let i = line + 1 ; i < lines . length ; i ++ ) {
822+ for ( let j = 0 ; j < lines [ i ] . length ; j ++ ) {
823+ return { line : i , index : j } ;
824+ }
825+ }
826+ }
827+
828+ return { line : null , index : null } ;
829+ }
830+
720831/**
721832 * If we didn't find a match on this line, back searches to find the previous
722833 * line that has a segment.
0 commit comments