@@ -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 ;
@@ -1693,45 +1697,87 @@ namespace FourSlash {
1693
1697
assert . equal ( actualDefinitionContainerName , expectedContainerName , this . messageAtLastKnownMarker ( "Definition Info Container Name" ) ) ;
1694
1698
}
1695
1699
1696
- public goToImplementation ( implIndex : number ) {
1700
+ public goToImplementation ( implIndex ? : number ) {
1697
1701
const implementations = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1698
1702
if ( ! implementations || ! implementations . length ) {
1699
1703
this . raiseError ( "goToImplementation failed - expected to at least one implementation location but got 0" ) ;
1700
1704
}
1701
1705
1706
+ if ( implIndex === undefined && implementations . length > 1 ) {
1707
+ this . raiseError ( `goToImplementation failed - no index given but more than 1 implementation returned (${ implementations . length } )` ) ;
1708
+ }
1709
+
1702
1710
if ( implIndex >= implementations . length ) {
1703
1711
this . raiseError ( `goToImplementation failed - implIndex value (${ implIndex } ) exceeds implementation list size (${ implementations . length } )` ) ;
1704
1712
}
1705
1713
1706
- const implementation = implementations [ implIndex ] ;
1714
+ const implementation = implementations [ implIndex || 0 ] ;
1707
1715
this . openFile ( implementation . fileName ) ;
1708
1716
this . currentCaretPosition = implementation . textSpan . start ;
1709
1717
}
1710
1718
1711
1719
public verifyRangesInImplementationList ( ) {
1712
- const implementations = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1720
+ const implementations : ImplementationLocationInformation [ ] = this . languageService . getImplementationAtPosition ( this . activeFile . fileName , this . currentCaretPosition ) ;
1713
1721
if ( ! implementations || ! implementations . length ) {
1714
1722
this . raiseError ( "verifyRangesInImplementationList failed - expected to at least one implementation location but got 0" ) ;
1715
1723
}
1716
1724
1725
+ for ( let i = 0 ; i < implementations . length ; i ++ ) {
1726
+ for ( let j = 0 ; j < implementations . length ; j ++ ) {
1727
+ if ( i !== j && implementationsAreEqual ( implementations [ i ] , implementations [ j ] ) ) {
1728
+ const { textSpan, fileName } = implementations [ i ] ;
1729
+ const end = textSpan . start + textSpan . length ;
1730
+ this . raiseError ( `Duplicate implementations returned for range (${ textSpan . start } , ${ end } ) in ${ fileName } ` ) ;
1731
+ }
1732
+ }
1733
+ }
1734
+
1717
1735
const ranges = this . getRanges ( ) ;
1718
1736
1719
1737
if ( ! ranges || ! ranges . length ) {
1720
1738
this . raiseError ( "verifyRangesInImplementationList failed - expected to at least one range in test source" ) ;
1721
1739
}
1722
1740
1741
+ const unsatisfiedRanges : Range [ ] = [ ] ;
1742
+
1723
1743
for ( const range of ranges ) {
1724
1744
let rangeIsPresent = false ;
1725
1745
const length = range . end - range . start ;
1726
1746
for ( const impl of implementations ) {
1727
1747
if ( range . fileName === impl . fileName && range . start === impl . textSpan . start && length === impl . textSpan . length ) {
1748
+ impl . matched = true ;
1728
1749
rangeIsPresent = true ;
1729
1750
break ;
1730
1751
}
1731
1752
}
1732
- assert . isTrue ( rangeIsPresent , `No implementation found for range ${ range . start } , ${ range . end } in ${ range . fileName } : ${ this . rangeText ( range ) } ` ) ;
1753
+ if ( ! rangeIsPresent ) {
1754
+ unsatisfiedRanges . push ( range ) ;
1755
+ }
1756
+ }
1757
+
1758
+ const unmatchedImplementations = implementations . filter ( impl => ! impl . matched ) ;
1759
+ if ( unmatchedImplementations . length || unsatisfiedRanges . length ) {
1760
+ let error = "Not all ranges or implementations are satisfied" ;
1761
+ if ( unsatisfiedRanges . length ) {
1762
+ error += "\nUnsatisfied ranges:" ;
1763
+ for ( const range of unsatisfiedRanges ) {
1764
+ error += `\n (${ range . start } , ${ range . end } ) in ${ range . fileName } : ${ this . rangeText ( range ) } ` ;
1765
+ }
1766
+ }
1767
+
1768
+ if ( unsatisfiedRanges . length ) {
1769
+ error += "\nUnmatched implementations:" ;
1770
+ for ( const impl of unmatchedImplementations ) {
1771
+ const end = impl . textSpan . start + impl . textSpan . length ;
1772
+ error += `\n (${ impl . textSpan . start } , ${ end } ) in ${ impl . fileName } : ${ this . getFileContent ( impl . fileName ) . slice ( impl . textSpan . start , end ) } ` ;
1773
+ }
1774
+ }
1775
+ this . raiseError ( error ) ;
1776
+ }
1777
+
1778
+ function implementationsAreEqual ( a : ImplementationLocationInformation , b : ImplementationLocationInformation ) {
1779
+ return a . fileName === b . fileName && TestState . textSpansEqual ( a . textSpan , b . textSpan ) ;
1733
1780
}
1734
- assert . equal ( implementations . length , ranges . length , `Different number of implementations (${ implementations . length } ) and ranges (${ ranges . length } )` ) ;
1735
1781
}
1736
1782
1737
1783
public getMarkers ( ) : Marker [ ] {
@@ -2911,7 +2957,7 @@ namespace FourSlashInterface {
2911
2957
this . state . goToTypeDefinition ( definitionIndex ) ;
2912
2958
}
2913
2959
2914
- public implementation ( implementationIndex = 0 ) {
2960
+ public implementation ( implementationIndex ?: number ) {
2915
2961
this . state . goToImplementation ( implementationIndex ) ;
2916
2962
}
2917
2963
0 commit comments