Skip to content

Commit 466d26f

Browse files
author
Paul van Brenk
committed
Fourslash support
1 parent 94ea825 commit 466d26f

7 files changed

Lines changed: 94 additions & 3 deletions

File tree

src/compiler/core.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ namespace ts {
115115
return -1;
116116
}
117117

118+
export function firstOrUndefined<T>(array: T[], predicate: (x: T) => boolean): T {
119+
for (let i = 0, len = array.length; i < len; i++) {
120+
if (predicate(array[i])) {
121+
return array[i];
122+
}
123+
}
124+
return undefined;
125+
}
126+
118127
export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number {
119128
for (let i = start || 0, len = text.length; i < len; i++) {
120129
if (contains(charCodes, text.charCodeAt(i))) {

src/compiler/diagnosticMessages.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,5 +3031,33 @@
30313031
"Unknown typing option '{0}'.": {
30323032
"category": "Error",
30333033
"code": 17010
3034+
},
3035+
"Add missing 'super()' call.": {
3036+
"category": "CodeFix",
3037+
"code": 90001
3038+
},
3039+
"Make 'super()' call the first statement in the constructor.": {
3040+
"category": "CodeFix",
3041+
"code": 90002
3042+
},
3043+
"Change 'extends' to 'implements'": {
3044+
"category": "CodeFix",
3045+
"code": 90003
3046+
},
3047+
"Remove unused identifiers": {
3048+
"category": "CodeFix",
3049+
"code": 90004
3050+
},
3051+
"Implement interface on reference": {
3052+
"category": "CodeFix",
3053+
"code": 90005
3054+
},
3055+
"Implement interface on class": {
3056+
"category": "CodeFix",
3057+
"code": 90006
3058+
},
3059+
"Implement inherited abstract class": {
3060+
"category": "CodeFix",
3061+
"code": 90007
30343062
}
30353063
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,6 +2547,7 @@ namespace ts {
25472547
Warning,
25482548
Error,
25492549
Message,
2550+
CodeFix,
25502551
}
25512552

25522553
export enum ModuleResolutionKind {

src/harness/fourslash.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ namespace FourSlash {
384384

385385
if (exists !== negative) {
386386
this.printErrorLog(negative, this.getAllDiagnostics());
387-
throw new Error("Failure between markers: " + startMarkerName + ", " + endMarkerName);
387+
throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`);
388388
}
389389
}
390390

@@ -638,7 +638,6 @@ namespace FourSlash {
638638
}
639639
}
640640

641-
642641
public verifyCompletionListAllowsNewIdentifier(negative: boolean) {
643642
const completions = this.getCompletionListAtCaret();
644643

@@ -1479,7 +1478,7 @@ namespace FourSlash {
14791478
if (isFormattingEdit) {
14801479
const newContent = this.getFileContent(fileName);
14811480

1482-
if (newContent.replace(/\s/g, "") !== oldContent.replace(/\s/g, "")) {
1481+
if (this.removeWhitespace(newContent) !== this.removeWhitespace(oldContent)) {
14831482
this.raiseError("Formatting operation destroyed non-whitespace content");
14841483
}
14851484
}
@@ -1545,6 +1544,10 @@ namespace FourSlash {
15451544
}
15461545
}
15471546

1547+
private removeWhitespace(text: string): string {
1548+
return text.replace(/\s/g, "");
1549+
}
1550+
15481551
public goToBOF() {
15491552
this.goToPosition(0);
15501553
}
@@ -1862,6 +1865,44 @@ namespace FourSlash {
18621865
}
18631866
}
18641867

1868+
public verifyCodeFixAtPosition(expectedText: string, errorCode?: number) {
1869+
1870+
const ranges = this.getRanges();
1871+
if (ranges.length == 0) {
1872+
this.raiseError("At least one range should be specified in the testfile.");
1873+
}
1874+
1875+
const fileName = this.activeFile.fileName;
1876+
const diagnostics = this.getDiagnostics(fileName);
1877+
1878+
if (diagnostics.length === 0) {
1879+
this.raiseError("Errors expected.");
1880+
}
1881+
1882+
if (diagnostics.length > 1 && !errorCode) {
1883+
this.raiseError("When there's more than one error, you must specify the errror to fix.");
1884+
}
1885+
1886+
const diagnostic = !errorCode ? diagnostics[0] : ts.firstOrUndefined(diagnostics, d => d.code == errorCode);
1887+
1888+
const actual = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [`TS${diagnostic.code}`]);
1889+
1890+
if (!actual || actual.length == 0) {
1891+
this.raiseError("No codefixes returned.");
1892+
}
1893+
1894+
if (actual.length > 1) {
1895+
this.raiseError("More than 1 codefix returned.");
1896+
}
1897+
1898+
this.applyEdits(actual[0].changes[0].fileName, actual[0].changes[0].textChanges, /*isFormattingEdit*/ false);
1899+
const actualText = this.rangeText(ranges[0]);
1900+
1901+
if (this.removeWhitespace(actualText) !== this.removeWhitespace(expectedText)) {
1902+
this.raiseError(`Actual text doesn't match expected text. Actual: '${actualText}' Expected: '${expectedText}'`);
1903+
}
1904+
}
1905+
18651906
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
18661907
const name = "verifyDocCommentTemplate";
18671908
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
@@ -3066,6 +3107,10 @@ namespace FourSlashInterface {
30663107
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
30673108
}
30683109

3110+
public codeFixAtPosition(expectedText: string, errorCode?: number): void {
3111+
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
3112+
}
3113+
30693114
public navigationBar(json: any) {
30703115
this.state.verifyNavigationBar(json);
30713116
}

src/harness/harnessLanguageService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,9 @@ namespace Harness.LanguageService {
453453
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
454454
return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace));
455455
}
456+
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] {
457+
return unwrapJSONCallResult(this.shim.getCodeFixesAtPosition(fileName, start, end, JSON.stringify(errorCodes)));
458+
}
456459
getEmitOutput(fileName: string): ts.EmitOutput {
457460
return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
458461
}

src/server/client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,10 @@ namespace ts.server {
592592
throw new Error("Not Implemented Yet.");
593593
}
594594

595+
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] {
596+
throw new Error("Not Implemented Yet.");
597+
}
598+
595599
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
596600
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
597601
const args: protocol.FileLocationRequestArgs = {

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ declare namespace FourSlashInterface {
192192
noMatchingBracePositionInCurrentFile(bracePosition: number): void;
193193
DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void;
194194
noDocCommentTemplate(): void;
195+
codeFixAtPosition(expectedText: string, errorCode?: number): void;
195196

196197
navigationBar(json: any): void;
197198
navigationItemsListCount(count: number, searchValue: string, matchKind?: string): void;

0 commit comments

Comments
 (0)