@@ -229,7 +229,7 @@ namespace FourSlash {
229
229
}
230
230
}
231
231
232
- constructor ( private originalInputFileName : string , private basePath : string , private testType : FourSlashTestType , public testData : FourSlashData ) {
232
+ constructor ( public originalInputFileName : string , private basePath : string , private testType : FourSlashTestType , public testData : FourSlashData ) {
233
233
// Create a new Services Adapter
234
234
this . cancellationToken = new TestCancellationToken ( ) ;
235
235
let compilationOptions = convertGlobalOptionsToCompilerOptions ( this . testData . globalOptions ) ;
@@ -998,16 +998,18 @@ namespace FourSlash {
998
998
references : ts . ReferenceEntry [ ] ;
999
999
}
1000
1000
interface RangeMarkerData {
1001
+ id ?: string ;
1001
1002
isWriteAccess ?: boolean ,
1002
1003
isDefinition ?: boolean ,
1003
1004
isInString ?: true ,
1004
1005
contextRangeIndex ?: number ,
1005
- contextRangeDelta ?: number
1006
+ contextRangeDelta ?: number ,
1007
+ contextRangeId ?: string
1006
1008
}
1007
1009
const fullExpected = ts . map < FourSlashInterface . ReferenceGroup , ReferenceGroupJson > ( parts , ( { definition, ranges } ) => ( {
1008
1010
definition : typeof definition === "string" ? definition : { ...definition , range : ts . createTextSpanFromRange ( definition . range ) } ,
1009
1011
references : ranges . map < ts . ReferenceEntry > ( r => {
1010
- const { isWriteAccess = false , isDefinition = false , isInString, contextRangeIndex, contextRangeDelta } = ( r . marker && r . marker . data || { } ) as RangeMarkerData ;
1012
+ const { isWriteAccess = false , isDefinition = false , isInString, contextRangeIndex, contextRangeDelta, contextRangeId } = ( r . marker && r . marker . data || { } ) as RangeMarkerData ;
1011
1013
let contextSpan : ts . TextSpan | undefined ;
1012
1014
if ( contextRangeDelta !== undefined ) {
1013
1015
const allRanges = this . getRanges ( ) ;
@@ -1016,15 +1018,22 @@ namespace FourSlash {
1016
1018
contextSpan = ts . createTextSpanFromRange ( allRanges [ index + contextRangeDelta ] ) ;
1017
1019
}
1018
1020
}
1021
+ else if ( contextRangeId !== undefined ) {
1022
+ const allRanges = this . getRanges ( ) ;
1023
+ const contextRange = ts . find ( allRanges , range => ( range . marker ?. data as RangeMarkerData ) ?. id === contextRangeId ) ;
1024
+ if ( contextRange ) {
1025
+ contextSpan = ts . createTextSpanFromRange ( contextRange ) ;
1026
+ }
1027
+ }
1019
1028
else if ( contextRangeIndex !== undefined ) {
1020
1029
contextSpan = ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) ;
1021
1030
}
1022
1031
return {
1023
- fileName : r . fileName ,
1024
1032
textSpan : ts . createTextSpanFromRange ( r ) ,
1033
+ fileName : r . fileName ,
1034
+ ...( contextSpan ? { contextSpan } : undefined ) ,
1025
1035
isWriteAccess,
1026
1036
isDefinition,
1027
- ...( contextSpan ? { contextSpan } : undefined ) ,
1028
1037
...( isInString ? { isInString : true } : undefined ) ,
1029
1038
} ;
1030
1039
} ) ,
@@ -1250,8 +1259,10 @@ namespace FourSlash {
1250
1259
1251
1260
public verifyRenameLocations ( startRanges : ArrayOrSingle < Range > , options : FourSlashInterface . RenameLocationsOptions ) {
1252
1261
interface RangeMarkerData {
1262
+ id ?: string ;
1253
1263
contextRangeIndex ?: number ,
1254
1264
contextRangeDelta ?: number
1265
+ contextRangeId ?: string ;
1255
1266
}
1256
1267
const { findInStrings = false , findInComments = false , ranges = this . getRanges ( ) , providePrefixAndSuffixTextForRename = true } = ts . isArray ( options ) ? { findInStrings : false , findInComments : false , ranges : options , providePrefixAndSuffixTextForRename : true } : options ;
1257
1268
@@ -1273,7 +1284,7 @@ namespace FourSlash {
1273
1284
locations && ts . sort ( locations , ( r1 , r2 ) => ts . compareStringsCaseSensitive ( r1 . fileName , r2 . fileName ) || r1 . textSpan . start - r2 . textSpan . start ) ;
1274
1285
assert . deepEqual ( sort ( references ) , sort ( ranges . map ( ( rangeOrOptions ) : ts . RenameLocation => {
1275
1286
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range : rangeOrOptions } ; // eslint-disable-line no-in-operator
1276
- const { contextRangeIndex, contextRangeDelta } = ( range . marker && range . marker . data || { } ) as RangeMarkerData ;
1287
+ const { contextRangeIndex, contextRangeDelta, contextRangeId } = ( range . marker && range . marker . data || { } ) as RangeMarkerData ;
1277
1288
let contextSpan : ts . TextSpan | undefined ;
1278
1289
if ( contextRangeDelta !== undefined ) {
1279
1290
const allRanges = this . getRanges ( ) ;
@@ -1282,6 +1293,13 @@ namespace FourSlash {
1282
1293
contextSpan = ts . createTextSpanFromRange ( allRanges [ index + contextRangeDelta ] ) ;
1283
1294
}
1284
1295
}
1296
+ else if ( contextRangeId !== undefined ) {
1297
+ const allRanges = this . getRanges ( ) ;
1298
+ const contextRange = ts . find ( allRanges , range => ( range . marker ?. data as RangeMarkerData ) ?. id === contextRangeId ) ;
1299
+ if ( contextRange ) {
1300
+ contextSpan = ts . createTextSpanFromRange ( contextRange ) ;
1301
+ }
1302
+ }
1285
1303
else if ( contextRangeIndex !== undefined ) {
1286
1304
contextSpan = ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) ;
1287
1305
}
@@ -3618,19 +3636,43 @@ namespace FourSlash {
3618
3636
// Parse out the files and their metadata
3619
3637
const testData = parseTestData ( absoluteBasePath , content , absoluteFileName ) ;
3620
3638
const state = new TestState ( absoluteFileName , absoluteBasePath , testType , testData ) ;
3621
- const output = ts . transpileModule ( content , { reportDiagnostics : true , compilerOptions : { target : ts . ScriptTarget . ES2015 } } ) ;
3639
+ const actualFileName = Harness . IO . resolvePath ( fileName ) || absoluteFileName ;
3640
+ const output = ts . transpileModule ( content , { reportDiagnostics : true , fileName : actualFileName , compilerOptions : { target : ts . ScriptTarget . ES2015 , sourceMap : true } } ) ;
3622
3641
if ( output . diagnostics ! . length > 0 ) {
3623
3642
throw new Error ( `Syntax error in ${ absoluteBasePath } : ${ output . diagnostics ! [ 0 ] . messageText } ` ) ;
3624
3643
}
3625
- runCode ( output . outputText , state ) ;
3644
+ runCode ( output , state , actualFileName ) ;
3626
3645
}
3627
3646
3628
- function runCode ( code : string , state : TestState ) : void {
3647
+ function runCode ( output : ts . TranspileOutput , state : TestState , fileName : string ) : void {
3629
3648
// Compile and execute the test
3630
- const wrappedCode =
3631
- `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {
3632
- ${ code }
3633
- })` ;
3649
+ const generatedFile = ts . changeExtension ( fileName , ".js" ) ;
3650
+ const mapFile = generatedFile + ".map" ;
3651
+ const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${ output . outputText } \n//# sourceURL=${ generatedFile } \n})` ;
3652
+
3653
+ type SourceMapSupportModule = typeof import ( "source-map-support" ) & {
3654
+ // TODO(rbuckton): This is missing from the DT definitions and needs to be added.
3655
+ resetRetrieveHandlers ( ) : void
3656
+ } ;
3657
+
3658
+ // Provide the content of the current test to 'source-map-support' so that it can give us the correct source positions
3659
+ // for test failures.
3660
+ let sourceMapSupportModule : SourceMapSupportModule | undefined ;
3661
+ try {
3662
+ sourceMapSupportModule = require ( "source-map-support" ) ;
3663
+ }
3664
+ catch {
3665
+ // do nothing
3666
+ }
3667
+
3668
+ sourceMapSupportModule ?. install ( {
3669
+ retrieveFile : path => {
3670
+ return path === generatedFile ? wrappedCode :
3671
+ path === mapFile ? output . sourceMapText ! :
3672
+ undefined ! ;
3673
+ }
3674
+ } ) ;
3675
+
3634
3676
try {
3635
3677
const test = new FourSlashInterface . Test ( state ) ;
3636
3678
const goTo = new FourSlashInterface . GoTo ( state ) ;
@@ -3645,8 +3687,13 @@ ${code}
3645
3687
f ( test , goTo , plugins , verify , edit , debug , format , cancellation , FourSlashInterface . Classification , FourSlashInterface . Completion , verifyOperationIsCancelled ) ;
3646
3688
}
3647
3689
catch ( err ) {
3690
+ // ensure we trigger 'source-map-support' while we still have the handler attached
3691
+ err . stack ?. toString ( ) ;
3648
3692
throw err ;
3649
3693
}
3694
+ finally {
3695
+ sourceMapSupportModule ?. resetRetrieveHandlers ( ) ;
3696
+ }
3650
3697
}
3651
3698
3652
3699
function chompLeadingSpace ( content : string ) {
@@ -3815,7 +3862,7 @@ ${code}
3815
3862
markerValue = JSON . parse ( "{ " + text + " }" ) ;
3816
3863
}
3817
3864
catch ( e ) {
3818
- reportError ( fileName , location . sourceLine , location . sourceColumn , "Unable to parse marker text " + e . message ) ;
3865
+ reportError ( fileName , location . sourceLine , location . sourceColumn , "Unable to parse marker text " + e . message + "\nSource:\n {| " + text + " |}" ) ;
3819
3866
}
3820
3867
3821
3868
if ( markerValue === undefined ) {
0 commit comments