diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 2a8b74d64bd..bf8fcdc4f84 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -3689,6 +3689,10 @@
"category": "Message",
"code": 90026
},
+ "Declare static property '{0}'.": {
+ "category": "Message",
+ "code": 90027
+ },
"Convert function to an ES2015 class": {
"category": "Message",
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index cb7988fb745..13db228e889 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -2337,6 +2337,39 @@ namespace FourSlash {
}
}
+ public verifyCodeFix(options: FourSlashInterface.VerifyCodeFixOptions) {
+ const fileName = this.activeFile.fileName;
+ const actions = this.getCodeFixActions(fileName, options.errorCode);
+ let index = options.index;
+ if (index === undefined) {
+ if (!(actions && actions.length === 1)) {
+ this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : ""}`);
+ }
+ index = 0;
+ }
+ else {
+ if (!(actions && actions.length >= index + 1)) {
+ this.raiseError(`Should find at least ${index + 1} codefix(es), but ${actions ? actions.length : "none"} found.`);
+ }
+ }
+
+ const action = actions[index];
+
+ assert.equal(action.description, options.description);
+
+ for (const change of action.changes) {
+ this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
+ }
+
+ if (options.newFileContent) {
+ assert(!options.newRangeContent);
+ this.verifyCurrentFileContent(options.newFileContent);
+ }
+ else {
+ this.verifyRangeIs(options.newRangeContent, /*includeWhitespace*/ true);
+ }
+ }
+
/**
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
* @param fileName Path to file where error should be retrieved from.
@@ -3716,6 +3749,10 @@ namespace FourSlashInterface {
this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges);
}
+ public codeFix(options: FourSlashInterface.VerifyCodeFixOptions) {
+ this.state.verifyCodeFix(options);
+ }
+
public codeFixAvailable() {
this.state.verifyCodeFixAvailable(this.negative);
}
@@ -4359,4 +4396,13 @@ namespace FourSlashInterface {
export interface CompletionsAtOptions {
isNewIdentifierLocation?: boolean;
}
+
+ export interface VerifyCodeFixOptions {
+ description: string;
+ // One of these should be defined.
+ newFileContent?: string;
+ newRangeContent?: string;
+ errorCode?: number;
+ index?: number;
+ }
}
diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts
index 97e209fe89a..a9583106fc6 100644
--- a/src/services/codefixes/fixAddMissingMember.ts
+++ b/src/services/codefixes/fixAddMissingMember.ts
@@ -156,8 +156,9 @@ namespace ts.codefix {
const propertyChangeTracker = textChanges.ChangeTracker.fromContext(context);
propertyChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, property, { suffix: context.newLineCharacter });
- (actions || (actions = [])).push({
- description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Declare_property_0), [tokenName]),
+ const diag = makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0;
+ actions = append(actions, {
+ description: formatStringFromArgs(getLocaleSpecificMessage(diag), [tokenName]),
changes: propertyChangeTracker.getChanges()
});
@@ -197,11 +198,9 @@ namespace ts.codefix {
const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromContext(context);
methodDeclarationChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, methodDeclaration, { suffix: context.newLineCharacter });
+ const diag = makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0;
return {
- description: formatStringFromArgs(getLocaleSpecificMessage(makeStatic ?
- Diagnostics.Declare_method_0 :
- Diagnostics.Declare_static_method_0),
- [tokenName]),
+ description: formatStringFromArgs(getLocaleSpecificMessage(diag), [tokenName]),
changes: methodDeclarationChangeTracker.getChanges()
};
}
diff --git a/tests/cases/fourslash/codeFixAddForgottenThis01.ts b/tests/cases/fourslash/codeFixAddForgottenThis01.ts
index 07f06348212..5dc5dfa4866 100644
--- a/tests/cases/fourslash/codeFixAddForgottenThis01.ts
+++ b/tests/cases/fourslash/codeFixAddForgottenThis01.ts
@@ -7,6 +7,9 @@
//// |]}
////}
-verify.rangeAfterCodeFix(`
+verify.codeFix({
+ description: "Add 'this.' to unresolved variable.",
+ newRangeContent: `
this.foo = 10;
- `, /*includeWhitespace*/ true);
\ No newline at end of file
+ `
+});
diff --git a/tests/cases/fourslash/codeFixAddForgottenThis02.ts b/tests/cases/fourslash/codeFixAddForgottenThis02.ts
index b387f349073..349df613452 100644
--- a/tests/cases/fourslash/codeFixAddForgottenThis02.ts
+++ b/tests/cases/fourslash/codeFixAddForgottenThis02.ts
@@ -6,4 +6,7 @@
//// bar() { [|foo = 10|] };
////}
-verify.rangeAfterCodeFix("this.foo = 10");
\ No newline at end of file
+verify.codeFix({
+ description: "Add 'this.' to unresolved variable.",
+ newRangeContent: "this.foo = 10",
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember.ts b/tests/cases/fourslash/codeFixAddMissingMember.ts
index 43c0bc3b765..a2ad3bd636e 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember.ts
@@ -1,14 +1,19 @@
///
-////[|class C {
+////class C {
//// method() {
//// this.foo = 10;
//// }
-////}|]
+////}
-verify.rangeAfterCodeFix(`class C {
- foo: number;
+verify.codeFix({
+ description: "Declare property 'foo'.",
+ index: 0,
+ // TODO: GH#18445
+ newFileContent: `class C {
+ foo: number;\r
method() {
this.foo = 10;
}
-}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
\ No newline at end of file
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember2.ts b/tests/cases/fourslash/codeFixAddMissingMember2.ts
index 7b64fde8c01..68c7035afcb 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember2.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember2.ts
@@ -1,14 +1,19 @@
///
-////[|class C {
+////class C {
//// method() {
//// this.foo = 10;
//// }
-////}|]
+////}
-verify.rangeAfterCodeFix(`class C {
- [x:string]: number;
+verify.codeFix({
+ description: "Add index signature for property 'foo'.",
+ index: 1,
+ // TODO: GH#18445
+ newFileContent: `class C {
+ [x: string]: number;\r
method() {
this.foo = 10;
}
-}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 1);
\ No newline at end of file
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember3.ts b/tests/cases/fourslash/codeFixAddMissingMember3.ts
index 47cacd6753a..1e3ff32f3fa 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember3.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember3.ts
@@ -1,14 +1,19 @@
///
-////[|class C {
+////class C {
//// static method() {
//// this.foo = 10;
//// }
-////}|]
+////}
-verify.rangeAfterCodeFix(`class C {
- static foo: number;
+verify.codeFix({
+ description: "Declare static property 'foo'.",
+ index: 0,
+ // TODO: GH#18445
+ newFileContent: `class C {
+ static foo: number;\r
static method() {
this.foo = 10;
}
-}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember4.ts b/tests/cases/fourslash/codeFixAddMissingMember4.ts
index 08df9ccabc2..cfbec8977f6 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember4.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember4.ts
@@ -4,19 +4,25 @@
// @allowJs: true
// @Filename: a.js
-////[|class C {
+////class C {
//// constructor() {
//// }
//// method() {
//// this.foo === 10;
//// }
-////}|]
+////}
-verify.rangeAfterCodeFix(`class C {
+verify.codeFix({
+ description: "Initialize property 'foo' in the constructor.",
+ index: 0,
+ // TODO: GH#18741 and GH#18445
+ newFileContent: `class C {
constructor() {
- this.foo = undefined;
+ \r
+this.foo = undefined;\r
}
method() {
this.foo === 10;
}
-}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
\ No newline at end of file
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts
index 44b11c5141b..804a3910a8c 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember5.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts
@@ -4,17 +4,20 @@
// @allowJs: true
// @Filename: a.js
-////[|class C {
+////class C {
//// static method() {
//// ()=>{ this.foo === 10 };
//// }
////}
-////|]
-verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 0);
-verify.currentFileContentIs(`class C {
+verify.codeFix({
+ description: "Initialize static property 'foo'.",
+ index: 0,
+ // TODO: GH#18743 and GH#18445
+ newFileContent: `class C {
static method() {
()=>{ this.foo === 10 };
}
-}
-C.foo = undefined;` + "\r\n"); // TODO: GH#18445
+}C.foo = undefined;\r
+`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember6.ts b/tests/cases/fourslash/codeFixAddMissingMember6.ts
index 2f3911d779e..2598014dde5 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember6.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember6.ts
@@ -4,15 +4,21 @@
// @allowJs: true
// @Filename: a.js
-////[|class C {
+////class C {
//// constructor() {
//// }
//// prop = ()=>{ this.foo === 10 };
-////}|]
+////}
-verify.rangeAfterCodeFix(`class C {
+verify.codeFix({
+ description: "Initialize property 'foo' in the constructor.",
+ index: 0,
+ // TODO: GH#18741 and GH#18445
+ newFileContent: `class C {
constructor() {
- this.foo = undefined;
- }
+ \r
+this.foo = undefined;\r
+}
prop = ()=>{ this.foo === 10 };
-}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
\ No newline at end of file
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts
index 7690023815c..4ed9c9293d7 100644
--- a/tests/cases/fourslash/codeFixAddMissingMember7.ts
+++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts
@@ -4,13 +4,16 @@
// @allowJs: true
// @Filename: a.js
-////[|class C {
+////class C {
//// static p = ()=>{ this.foo === 10 };
////}
-////|]
-verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2)
-verify.currentFileContentIs(`class C {
+verify.codeFix({
+ description: "Initialize static property 'foo'.",
+ index: 2,
+ // TODO: GH#18743 and GH#18445
+ newFileContent: `class C {
static p = ()=>{ this.foo === 10 };
-}
-C.foo = undefined;` + "\r\n"); // TODO: GH#18445
+}C.foo = undefined;\r
+`
+});
diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
index 75be5420e43..e37725c01ed 100644
--- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
+++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
@@ -9,18 +9,55 @@
//// }
//// }
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
+verify.codeFix({
+ description: "Declare static method 'm1'.",
+ index: 0,
+ // TODO: GH#18445
+ newRangeContent: `
+ static m1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `,
+});
-verify.rangeIs(`
- static prop2: string;
- static prop1: number;
- static m2(arg0: any, arg1: any): any {
- throw new Error("Method not implemented.");
- }
- static m1(arg0: any, arg1: any, arg2: any): any {
- throw new Error("Method not implemented.");
- }
-`);
+verify.codeFix({
+ description: "Declare static method 'm2'.",
+ index: 0,
+ newRangeContent: `
+ static m2(arg0: any, arg1: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ static m1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `,
+});
+
+verify.codeFix({
+ description: "Declare static property 'prop1'.",
+ index: 0,
+ newRangeContent: `
+ static prop1: number;\r
+ static m2(arg0: any, arg1: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ static m1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `,
+});
+
+verify.codeFix({
+ description: "Declare static property 'prop2'.",
+ index: 0,
+ newRangeContent: `
+ static prop2: string;\r
+ static prop1: number;\r
+ static m2(arg0: any, arg1: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ static m1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `,
+});
diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts
index 6a61f8421aa..3aace334b36 100644
--- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts
+++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts
@@ -10,18 +10,42 @@
//// }
//// }
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
-verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
+verify.codeFix({
+ description: "Declare method 'foo1'.",
+ index: 0,
+ // TODO: GH#18445
+ newRangeContent: `
+ foo1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `,
+});
-verify.rangeIs(`
- foo3(): any {
- throw new Error("Method not implemented.");
- }
- foo2(): any {
- throw new Error("Method not implemented.");
- }
- foo1(arg0: any, arg1: any, arg2: any): any {
- throw new Error("Method not implemented.");
- }
-`);
\ No newline at end of file
+verify.codeFix({
+ description: "Declare method 'foo2'.",
+ index: 0,
+ newRangeContent: `
+ foo2(): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ foo1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `
+});
+
+verify.codeFix({
+ description: "Declare method 'foo3'.",
+ index: 0,
+ newRangeContent:`
+ foo3(): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ foo2(): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ foo1(arg0: any, arg1: any, arg2: any): any {\r
+ throw new Error("Method not implemented.");\r
+ }\r
+ `
+});
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index b3d29456569..f4d47abc9c9 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -155,6 +155,13 @@ declare namespace FourSlashInterface {
implementationListIsEmpty(): void;
isValidBraceCompletionAtPosition(openingBrace?: string): void;
isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void;
+ codeFix(options: {
+ description: string,
+ newFileContent?: string,
+ newRangeContent?: string,
+ errorCode?: number,
+ index?: number,
+ });
codeFixAvailable(): void;
applicableRefactorAvailableAtMarker(markerName: string): void;
codeFixDiagnosticsAvailableAtMarkers(markerNames: string[], diagnosticCode?: number): void;