@@ -1003,18 +1003,43 @@ namespace FourSlash {
1003
1003
definition : string | { text : string , range : ts . TextSpan } ;
1004
1004
references : ts . ReferenceEntry [ ] ;
1005
1005
}
1006
+ interface RangeMarkerData {
1007
+ id ?: string ;
1008
+ isWriteAccess ?: boolean ,
1009
+ isDefinition ?: boolean ,
1010
+ isInString ?: true ,
1011
+ contextRangeIndex ?: number ,
1012
+ contextRangeDelta ?: number ,
1013
+ contextRangeId ?: string
1014
+ }
1006
1015
const fullExpected = ts . map < FourSlashInterface . ReferenceGroup , ReferenceGroupJson > ( parts , ( { definition, ranges } ) => ( {
1007
1016
definition : typeof definition === "string" ? definition : { ...definition , range : ts . createTextSpanFromRange ( definition . range ) } ,
1008
1017
references : ranges . map < ts . ReferenceEntry > ( r => {
1009
- const { isWriteAccess = false , isDefinition = false , isInString, contextRangeIndex } = ( r . marker && r . marker . data || { } ) as { isWriteAccess ?: boolean , isDefinition ?: boolean , isInString ?: true , contextRangeIndex ?: number } ;
1018
+ const { isWriteAccess = false , isDefinition = false , isInString, contextRangeIndex, contextRangeDelta, contextRangeId } = ( r . marker && r . marker . data || { } ) as RangeMarkerData ;
1019
+ let contextSpan : ts . TextSpan | undefined ;
1020
+ if ( contextRangeDelta !== undefined ) {
1021
+ const allRanges = this . getRanges ( ) ;
1022
+ const index = allRanges . indexOf ( r ) ;
1023
+ if ( index !== - 1 ) {
1024
+ contextSpan = ts . createTextSpanFromRange ( allRanges [ index + contextRangeDelta ] ) ;
1025
+ }
1026
+ }
1027
+ else if ( contextRangeId !== undefined ) {
1028
+ const allRanges = this . getRanges ( ) ;
1029
+ const contextRange = ts . find ( allRanges , range => ( range . marker ?. data as RangeMarkerData ) ?. id === contextRangeId ) ;
1030
+ if ( contextRange ) {
1031
+ contextSpan = ts . createTextSpanFromRange ( contextRange ) ;
1032
+ }
1033
+ }
1034
+ else if ( contextRangeIndex !== undefined ) {
1035
+ contextSpan = ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) ;
1036
+ }
1010
1037
return {
1011
- fileName : r . fileName ,
1012
1038
textSpan : ts . createTextSpanFromRange ( r ) ,
1039
+ fileName : r . fileName ,
1040
+ ...( contextSpan ? { contextSpan } : undefined ) ,
1013
1041
isWriteAccess,
1014
1042
isDefinition,
1015
- ...( contextRangeIndex !== undefined ?
1016
- { contextSpan : ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) } :
1017
- undefined ) ,
1018
1043
...( isInString ? { isInString : true } : undefined ) ,
1019
1044
} ;
1020
1045
} ) ,
@@ -1038,7 +1063,7 @@ namespace FourSlash {
1038
1063
}
1039
1064
1040
1065
public verifyNoReferences ( markerNameOrRange ?: string | Range ) {
1041
- if ( markerNameOrRange ) this . goToMarkerOrRange ( markerNameOrRange ) ;
1066
+ if ( markerNameOrRange !== undefined ) this . goToMarkerOrRange ( markerNameOrRange ) ;
1042
1067
const refs = this . getReferencesAtCaret ( ) ;
1043
1068
if ( refs && refs . length ) {
1044
1069
this . raiseError ( `Expected getReferences to fail, but saw references: ${ stringify ( refs ) } ` ) ;
@@ -1239,6 +1264,12 @@ namespace FourSlash {
1239
1264
}
1240
1265
1241
1266
public verifyRenameLocations ( startRanges : ArrayOrSingle < Range > , options : FourSlashInterface . RenameLocationsOptions ) {
1267
+ interface RangeMarkerData {
1268
+ id ?: string ;
1269
+ contextRangeIndex ?: number ,
1270
+ contextRangeDelta ?: number
1271
+ contextRangeId ?: string ;
1272
+ }
1242
1273
const { findInStrings = false , findInComments = false , ranges = this . getRanges ( ) , providePrefixAndSuffixTextForRename = true } = ts . isArray ( options ) ? { findInStrings : false , findInComments : false , ranges : options , providePrefixAndSuffixTextForRename : true } : options ;
1243
1274
1244
1275
const _startRanges = toArray ( startRanges ) ;
@@ -1259,13 +1290,29 @@ namespace FourSlash {
1259
1290
locations && ts . sort ( locations , ( r1 , r2 ) => ts . compareStringsCaseSensitive ( r1 . fileName , r2 . fileName ) || r1 . textSpan . start - r2 . textSpan . start ) ;
1260
1291
assert . deepEqual ( sort ( references ) , sort ( ranges . map ( ( rangeOrOptions ) : ts . RenameLocation => {
1261
1292
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range : rangeOrOptions } ; // eslint-disable-line no-in-operator
1262
- const { contextRangeIndex } = ( range . marker && range . marker . data || { } ) as { contextRangeIndex ?: number ; } ;
1293
+ const { contextRangeIndex, contextRangeDelta, contextRangeId } = ( range . marker && range . marker . data || { } ) as RangeMarkerData ;
1294
+ let contextSpan : ts . TextSpan | undefined ;
1295
+ if ( contextRangeDelta !== undefined ) {
1296
+ const allRanges = this . getRanges ( ) ;
1297
+ const index = allRanges . indexOf ( range ) ;
1298
+ if ( index !== - 1 ) {
1299
+ contextSpan = ts . createTextSpanFromRange ( allRanges [ index + contextRangeDelta ] ) ;
1300
+ }
1301
+ }
1302
+ else if ( contextRangeId !== undefined ) {
1303
+ const allRanges = this . getRanges ( ) ;
1304
+ const contextRange = ts . find ( allRanges , range => ( range . marker ?. data as RangeMarkerData ) ?. id === contextRangeId ) ;
1305
+ if ( contextRange ) {
1306
+ contextSpan = ts . createTextSpanFromRange ( contextRange ) ;
1307
+ }
1308
+ }
1309
+ else if ( contextRangeIndex !== undefined ) {
1310
+ contextSpan = ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) ;
1311
+ }
1263
1312
return {
1264
1313
fileName : range . fileName ,
1265
1314
textSpan : ts . createTextSpanFromRange ( range ) ,
1266
- ...( contextRangeIndex !== undefined ?
1267
- { contextSpan : ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) } :
1268
- undefined ) ,
1315
+ ...( contextSpan ? { contextSpan } : undefined ) ,
1269
1316
...prefixSuffixText
1270
1317
} ;
1271
1318
} ) ) ) ;
@@ -3595,19 +3642,41 @@ namespace FourSlash {
3595
3642
// Parse out the files and their metadata
3596
3643
const testData = parseTestData ( absoluteBasePath , content , absoluteFileName ) ;
3597
3644
const state = new TestState ( absoluteFileName , absoluteBasePath , testType , testData ) ;
3598
- const output = ts . transpileModule ( content , { reportDiagnostics : true , compilerOptions : { target : ts . ScriptTarget . ES2015 } } ) ;
3645
+ const actualFileName = Harness . IO . resolvePath ( fileName ) || absoluteFileName ;
3646
+ const output = ts . transpileModule ( content , { reportDiagnostics : true , fileName : actualFileName , compilerOptions : { target : ts . ScriptTarget . ES2015 , inlineSourceMap : true } } ) ;
3599
3647
if ( output . diagnostics ! . length > 0 ) {
3600
3648
throw new Error ( `Syntax error in ${ absoluteBasePath } : ${ output . diagnostics ! [ 0 ] . messageText } ` ) ;
3601
3649
}
3602
- runCode ( output . outputText , state ) ;
3650
+ runCode ( output . outputText , state , actualFileName ) ;
3603
3651
}
3604
3652
3605
- function runCode ( code : string , state : TestState ) : void {
3653
+ function runCode ( code : string , state : TestState , fileName : string ) : void {
3606
3654
// Compile and execute the test
3607
- const wrappedCode =
3608
- `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {
3609
- ${ code }
3610
- })` ;
3655
+ const generatedFile = ts . changeExtension ( fileName , ".js" ) ;
3656
+ const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${ code } \n//# sourceURL=${ generatedFile } \n})` ;
3657
+
3658
+ type SourceMapSupportModule = typeof import ( "source-map-support" ) & {
3659
+ // TODO(rbuckton): This is missing from the DT definitions and needs to be added.
3660
+ resetRetrieveHandlers ( ) : void
3661
+ } ;
3662
+
3663
+ // Provide the content of the current test to 'source-map-support' so that it can give us the correct source positions
3664
+ // for test failures.
3665
+ let sourceMapSupportModule : SourceMapSupportModule | undefined ;
3666
+ try {
3667
+ sourceMapSupportModule = require ( "source-map-support" ) ;
3668
+ }
3669
+ catch {
3670
+ // do nothing
3671
+ }
3672
+
3673
+ sourceMapSupportModule ?. install ( {
3674
+ retrieveFile : path => {
3675
+ return path === generatedFile ? wrappedCode :
3676
+ undefined ! ;
3677
+ }
3678
+ } ) ;
3679
+
3611
3680
try {
3612
3681
const test = new FourSlashInterface . Test ( state ) ;
3613
3682
const goTo = new FourSlashInterface . GoTo ( state ) ;
@@ -3622,8 +3691,13 @@ ${code}
3622
3691
f ( test , goTo , plugins , verify , edit , debug , format , cancellation , FourSlashInterface . Classification , FourSlashInterface . Completion , verifyOperationIsCancelled ) ;
3623
3692
}
3624
3693
catch ( err ) {
3694
+ // ensure 'source-map-support' is triggered while we still have the handler attached by accessing `error.stack`.
3695
+ err . stack ?. toString ( ) ;
3625
3696
throw err ;
3626
3697
}
3698
+ finally {
3699
+ sourceMapSupportModule ?. resetRetrieveHandlers ( ) ;
3700
+ }
3627
3701
}
3628
3702
3629
3703
function chompLeadingSpace ( content : string ) {
0 commit comments