Skip to content

Commit 1cdd1d3

Browse files
committed
Code cleanup and a few edge cases
1 parent 5913a35 commit 1cdd1d3

10 files changed

+217
-114
lines changed

src/harness/fourslash.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ namespace FourSlash {
8888
marker?: Marker;
8989
}
9090

91+
interface ImplementationLocationInformation extends ts.ImplementationLocation {
92+
matched?: boolean;
93+
}
94+
9195
export interface TextSpan {
9296
start: number;
9397
end: number;
@@ -1693,45 +1697,87 @@ namespace FourSlash {
16931697
assert.equal(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name"));
16941698
}
16951699

1696-
public goToImplementation(implIndex: number) {
1700+
public goToImplementation(implIndex?: number) {
16971701
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
16981702
if (!implementations || !implementations.length) {
16991703
this.raiseError("goToImplementation failed - expected to at least one implementation location but got 0");
17001704
}
17011705

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+
17021710
if (implIndex >= implementations.length) {
17031711
this.raiseError(`goToImplementation failed - implIndex value (${implIndex}) exceeds implementation list size (${implementations.length})`);
17041712
}
17051713

1706-
const implementation = implementations[implIndex];
1714+
const implementation = implementations[implIndex || 0];
17071715
this.openFile(implementation.fileName);
17081716
this.currentCaretPosition = implementation.textSpan.start;
17091717
}
17101718

17111719
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);
17131721
if (!implementations || !implementations.length) {
17141722
this.raiseError("verifyRangesInImplementationList failed - expected to at least one implementation location but got 0");
17151723
}
17161724

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+
17171735
const ranges = this.getRanges();
17181736

17191737
if (!ranges || !ranges.length) {
17201738
this.raiseError("verifyRangesInImplementationList failed - expected to at least one range in test source");
17211739
}
17221740

1741+
const unsatisfiedRanges: Range[] = [];
1742+
17231743
for (const range of ranges) {
17241744
let rangeIsPresent = false;
17251745
const length = range.end - range.start;
17261746
for (const impl of implementations) {
17271747
if (range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length) {
1748+
impl.matched = true;
17281749
rangeIsPresent = true;
17291750
break;
17301751
}
17311752
}
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);
17331780
}
1734-
assert.equal(implementations.length, ranges.length, `Different number of implementations (${implementations.length}) and ranges (${ranges.length})`);
17351781
}
17361782

17371783
public getMarkers(): Marker[] {
@@ -2911,7 +2957,7 @@ namespace FourSlashInterface {
29112957
this.state.goToTypeDefinition(definitionIndex);
29122958
}
29132959

2914-
public implementation(implementationIndex = 0) {
2960+
public implementation(implementationIndex?: number) {
29152961
this.state.goToImplementation(implementationIndex);
29162962
}
29172963

0 commit comments

Comments
 (0)