@@ -1003,18 +1003,43 @@ namespace FourSlash {
10031003 definition : string | { text : string , range : ts . TextSpan } ;
10041004 references : ts . ReferenceEntry [ ] ;
10051005 }
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+ }
10061015 const fullExpected = ts . map < FourSlashInterface . ReferenceGroup , ReferenceGroupJson > ( parts , ( { definition, ranges } ) => ( {
10071016 definition : typeof definition === "string" ? definition : { ...definition , range : ts . createTextSpanFromRange ( definition . range ) } ,
10081017 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+ }
10101037 return {
1011- fileName : r . fileName ,
10121038 textSpan : ts . createTextSpanFromRange ( r ) ,
1039+ fileName : r . fileName ,
1040+ ...( contextSpan ? { contextSpan } : undefined ) ,
10131041 isWriteAccess,
10141042 isDefinition,
1015- ...( contextRangeIndex !== undefined ?
1016- { contextSpan : ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) } :
1017- undefined ) ,
10181043 ...( isInString ? { isInString : true } : undefined ) ,
10191044 } ;
10201045 } ) ,
@@ -1038,7 +1063,7 @@ namespace FourSlash {
10381063 }
10391064
10401065 public verifyNoReferences ( markerNameOrRange ?: string | Range ) {
1041- if ( markerNameOrRange ) this . goToMarkerOrRange ( markerNameOrRange ) ;
1066+ if ( markerNameOrRange !== undefined ) this . goToMarkerOrRange ( markerNameOrRange ) ;
10421067 const refs = this . getReferencesAtCaret ( ) ;
10431068 if ( refs && refs . length ) {
10441069 this . raiseError ( `Expected getReferences to fail, but saw references: ${ stringify ( refs ) } ` ) ;
@@ -1239,6 +1264,12 @@ namespace FourSlash {
12391264 }
12401265
12411266 public verifyRenameLocations ( startRanges : ArrayOrSingle < Range > , options : FourSlashInterface . RenameLocationsOptions ) {
1267+ interface RangeMarkerData {
1268+ id ?: string ;
1269+ contextRangeIndex ?: number ,
1270+ contextRangeDelta ?: number
1271+ contextRangeId ?: string ;
1272+ }
12421273 const { findInStrings = false , findInComments = false , ranges = this . getRanges ( ) , providePrefixAndSuffixTextForRename = true } = ts . isArray ( options ) ? { findInStrings : false , findInComments : false , ranges : options , providePrefixAndSuffixTextForRename : true } : options ;
12431274
12441275 const _startRanges = toArray ( startRanges ) ;
@@ -1259,13 +1290,29 @@ namespace FourSlash {
12591290 locations && ts . sort ( locations , ( r1 , r2 ) => ts . compareStringsCaseSensitive ( r1 . fileName , r2 . fileName ) || r1 . textSpan . start - r2 . textSpan . start ) ;
12601291 assert . deepEqual ( sort ( references ) , sort ( ranges . map ( ( rangeOrOptions ) : ts . RenameLocation => {
12611292 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+ }
12631312 return {
12641313 fileName : range . fileName ,
12651314 textSpan : ts . createTextSpanFromRange ( range ) ,
1266- ...( contextRangeIndex !== undefined ?
1267- { contextSpan : ts . createTextSpanFromRange ( this . getRanges ( ) [ contextRangeIndex ] ) } :
1268- undefined ) ,
1315+ ...( contextSpan ? { contextSpan } : undefined ) ,
12691316 ...prefixSuffixText
12701317 } ;
12711318 } ) ) ) ;
@@ -3595,19 +3642,41 @@ namespace FourSlash {
35953642 // Parse out the files and their metadata
35963643 const testData = parseTestData ( absoluteBasePath , content , absoluteFileName ) ;
35973644 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 } } ) ;
35993647 if ( output . diagnostics ! . length > 0 ) {
36003648 throw new Error ( `Syntax error in ${ absoluteBasePath } : ${ output . diagnostics ! [ 0 ] . messageText } ` ) ;
36013649 }
3602- runCode ( output . outputText , state ) ;
3650+ runCode ( output . outputText , state , actualFileName ) ;
36033651 }
36043652
3605- function runCode ( code : string , state : TestState ) : void {
3653+ function runCode ( code : string , state : TestState , fileName : string ) : void {
36063654 // 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+
36113680 try {
36123681 const test = new FourSlashInterface . Test ( state ) ;
36133682 const goTo = new FourSlashInterface . GoTo ( state ) ;
@@ -3622,8 +3691,13 @@ ${code}
36223691 f ( test , goTo , plugins , verify , edit , debug , format , cancellation , FourSlashInterface . Classification , FourSlashInterface . Completion , verifyOperationIsCancelled ) ;
36233692 }
36243693 catch ( err ) {
3694+ // ensure 'source-map-support' is triggered while we still have the handler attached by accessing `error.stack`.
3695+ err . stack ?. toString ( ) ;
36253696 throw err ;
36263697 }
3698+ finally {
3699+ sourceMapSupportModule ?. resetRetrieveHandlers ( ) ;
3700+ }
36273701 }
36283702
36293703 function chompLeadingSpace ( content : string ) {
0 commit comments