@@ -88,6 +88,10 @@ namespace FourSlash {
88
88
marker ?: Marker ;
89
89
}
90
90
91
+ interface ImplementationLocationInformation extends ts . ImplementationLocation {
92
+ matched ?: boolean ;
93
+ }
94
+
91
95
export interface TextSpan {
92
96
start : number ;
93
97
end : number ;
@@ -1699,6 +1703,17 @@ namespace FourSlash {
1699
1703
assertFn ( actualCount , expectedCount , this . messageAtLastKnownMarker ( "Type definitions Count" ) ) ;
1700
1704
}
1701
1705
1706
+ public verifyImplementationListIsEmpty ( negative : boolean ) {
1707
+ const implementations = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1708
+
1709
+ if ( negative ) {
1710
+ assert . isTrue ( implementations && implementations . length > 0 , "Expected at least one implementation but got 0" ) ;
1711
+ }
1712
+ else {
1713
+ assert . isUndefined ( implementations , "Expected implementation list to be empty but implementations returned" ) ;
1714
+ }
1715
+ }
1716
+
1702
1717
public verifyGoToDefinitionName ( expectedName : string , expectedContainerName : string ) {
1703
1718
const definitions = this . languageService . getDefinitionAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1704
1719
const actualDefinitionName = definitions && definitions . length ? definitions [ 0 ] . name : "" ;
@@ -1707,6 +1722,82 @@ namespace FourSlash {
1707
1722
assert . equal ( actualDefinitionContainerName , expectedContainerName , this . messageAtLastKnownMarker ( "Definition Info Container Name" ) ) ;
1708
1723
}
1709
1724
1725
+ public goToImplementation ( ) {
1726
+ const implementations = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1727
+ if ( ! implementations || ! implementations . length ) {
1728
+ this . raiseError ( "goToImplementation failed - expected to find at least one implementation location but got 0" ) ;
1729
+ }
1730
+ if ( implementations . length > 1 ) {
1731
+ this . raiseError ( `goToImplementation failed - more than 1 implementation returned (${ implementations . length } )` ) ;
1732
+ }
1733
+
1734
+ const implementation = implementations [ 0 ] ;
1735
+ this . openFile ( implementation . fileName ) ;
1736
+ this . currentCaretPosition = implementation . textSpan . start ;
1737
+ }
1738
+
1739
+ public verifyRangesInImplementationList ( markerName : string ) {
1740
+ this . goToMarker ( markerName ) ;
1741
+ const implementations : ImplementationLocationInformation [ ] = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1742
+ if ( ! implementations || ! implementations . length ) {
1743
+ this . raiseError ( "verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0" ) ;
1744
+ }
1745
+
1746
+ for ( let i = 0 ; i < implementations . length ; i ++ ) {
1747
+ for ( let j = 0 ; j < implementations . length ; j ++ ) {
1748
+ if ( i !== j && implementationsAreEqual ( implementations [ i ] , implementations [ j ] ) ) {
1749
+ const { textSpan, fileName } = implementations [ i ] ;
1750
+ const end = textSpan . start + textSpan . length ;
1751
+ this . raiseError ( `Duplicate implementations returned for range (${ textSpan . start } , ${ end } ) in ${ fileName } ` ) ;
1752
+ }
1753
+ }
1754
+ }
1755
+
1756
+ const ranges = this . getRanges ( ) ;
1757
+
1758
+ if ( ! ranges || ! ranges . length ) {
1759
+ this . raiseError ( "verifyRangesInImplementationList failed - expected to find at least one range in test source" ) ;
1760
+ }
1761
+
1762
+ const unsatisfiedRanges : Range [ ] = [ ] ;
1763
+
1764
+ for ( const range of ranges ) {
1765
+ const length = range . end - range . start ;
1766
+ const matchingImpl = ts . find ( implementations , impl =>
1767
+ range . fileName === impl . fileName && range . start === impl . textSpan . start && length === impl . textSpan . length ) ;
1768
+ if ( matchingImpl ) {
1769
+ matchingImpl . matched = true ;
1770
+ }
1771
+ else {
1772
+ unsatisfiedRanges . push ( range ) ;
1773
+ }
1774
+ }
1775
+
1776
+ const unmatchedImplementations = implementations . filter ( impl => ! impl . matched ) ;
1777
+ if ( unmatchedImplementations . length || unsatisfiedRanges . length ) {
1778
+ let error = "Not all ranges or implementations are satisfied" ;
1779
+ if ( unsatisfiedRanges . length ) {
1780
+ error += "\nUnsatisfied ranges:" ;
1781
+ for ( const range of unsatisfiedRanges ) {
1782
+ error += `\n (${ range . start } , ${ range . end } ) in ${ range . fileName } : ${ this . rangeText ( range ) } ` ;
1783
+ }
1784
+ }
1785
+
1786
+ if ( unmatchedImplementations . length ) {
1787
+ error += "\nUnmatched implementations:" ;
1788
+ for ( const impl of unmatchedImplementations ) {
1789
+ const end = impl . textSpan . start + impl . textSpan . length ;
1790
+ error += `\n (${ impl . textSpan . start } , ${ end } ) in ${ impl . fileName } : ${ this . getFileContent ( impl . fileName ) . slice ( impl . textSpan . start , end ) } ` ;
1791
+ }
1792
+ }
1793
+ this . raiseError ( error ) ;
1794
+ }
1795
+
1796
+ function implementationsAreEqual ( a : ImplementationLocationInformation , b : ImplementationLocationInformation ) {
1797
+ return a . fileName === b . fileName && TestState . textSpansEqual ( a . textSpan , b . textSpan ) ;
1798
+ }
1799
+ }
1800
+
1710
1801
public getMarkers ( ) : Marker [ ] {
1711
1802
// Return a copy of the list
1712
1803
return this . testData . markers . slice ( 0 ) ;
@@ -2885,6 +2976,10 @@ namespace FourSlashInterface {
2885
2976
this . state . goToTypeDefinition ( definitionIndex ) ;
2886
2977
}
2887
2978
2979
+ public implementation ( ) {
2980
+ this . state . goToImplementation ( ) ;
2981
+ }
2982
+
2888
2983
public position ( position : number , fileIndex ?: number ) : void ;
2889
2984
public position ( position : number , fileName ?: string ) : void ;
2890
2985
public position ( position : number , fileNameOrIndex ?: any ) : void {
@@ -2985,6 +3080,10 @@ namespace FourSlashInterface {
2985
3080
this . state . verifyTypeDefinitionsCount ( this . negative , expectedCount ) ;
2986
3081
}
2987
3082
3083
+ public implementationListIsEmpty ( ) {
3084
+ this . state . verifyImplementationListIsEmpty ( this . negative ) ;
3085
+ }
3086
+
2988
3087
public isValidBraceCompletionAtPosition ( openingBrace : string ) {
2989
3088
this . state . verifyBraceCompletionAtPosition ( this . negative , openingBrace ) ;
2990
3089
}
@@ -3253,6 +3352,10 @@ namespace FourSlashInterface {
3253
3352
public ProjectInfo ( expected : string [ ] ) {
3254
3353
this . state . verifyProjectInfo ( expected ) ;
3255
3354
}
3355
+
3356
+ public allRangesAppearInImplementationList ( markerName : string ) {
3357
+ this . state . verifyRangesInImplementationList ( markerName ) ;
3358
+ }
3256
3359
}
3257
3360
3258
3361
export class Edit {
0 commit comments