Skip to content

Commit 3e32e15

Browse files
author
Andy
authored
Add 'fixAllDescription' property to CodeFixAction (#22616)
* Add 'fixAllDescription' property to CodeFixAction * Code review * Add to protocol * Make fixAllDescription be just a string
1 parent 2cbad6a commit 3e32e15

File tree

58 files changed

+258
-149
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+258
-149
lines changed

src/compiler/diagnosticMessages.json

+96
Original file line numberDiff line numberDiff line change
@@ -4026,5 +4026,101 @@
40264026
"Add definite assignment assertion to property '{0}'": {
40274027
"category": "Message",
40284028
"code": 95020
4029+
},
4030+
"Add all missing members": {
4031+
"category": "Message",
4032+
"code": 95022
4033+
},
4034+
"Infer all types from usage": {
4035+
"category": "Message",
4036+
"code": 95023
4037+
},
4038+
"Delete all unused declarations": {
4039+
"category": "Message",
4040+
"code": 95024
4041+
},
4042+
"Prefix all unused declarations with '_' where possible": {
4043+
"category": "Message",
4044+
"code": 95025
4045+
},
4046+
"Fix all detected spelling errors": {
4047+
"category": "Message",
4048+
"code": 95026
4049+
},
4050+
"Add initializers to all uninitialized properties": {
4051+
"category": "Message",
4052+
"code": 95027
4053+
},
4054+
"Add definite assignment assertions to all uninitialized properties": {
4055+
"category": "Message",
4056+
"code": 95028
4057+
},
4058+
"Add undefined type to all uninitialized properties": {
4059+
"category": "Message",
4060+
"code": 95029
4061+
},
4062+
"Change all jsdoc-style types to TypeScript": {
4063+
"category": "Message",
4064+
"code": 95030
4065+
},
4066+
"Change all jsdoc-style types to TypeScript (and add '| undefined' to nullable types)": {
4067+
"category": "Message",
4068+
"code": 95031
4069+
},
4070+
"Implement all unimplemented interfaces": {
4071+
"category": "Message",
4072+
"code": 95032
4073+
},
4074+
"Install all missing types packages": {
4075+
"category": "Message",
4076+
"code": 95033
4077+
},
4078+
"Rewrite all as indexed access types": {
4079+
"category": "Message",
4080+
"code": 95034
4081+
},
4082+
"Convert all to default imports": {
4083+
"category": "Message",
4084+
"code": 95035
4085+
},
4086+
"Make all 'super()' calls the first statement in their constructor": {
4087+
"category": "Message",
4088+
"code": 95036
4089+
},
4090+
"Add 'this.' to all unresolved variables matching a member name": {
4091+
"category": "Message",
4092+
"code": 95037
4093+
},
4094+
"Change all extended interfaces to 'implements'": {
4095+
"category": "Message",
4096+
"code": 95038
4097+
},
4098+
"Add all missing super calls": {
4099+
"category": "Message",
4100+
"code": 95039
4101+
},
4102+
"Implement all inherited abstract classes": {
4103+
"category": "Message",
4104+
"code": 95040
4105+
},
4106+
"Add all missing 'async' modifiers": {
4107+
"category": "Message",
4108+
"code": 95041
4109+
},
4110+
"Add '@ts-ignore' to all error messages": {
4111+
"category": "Message",
4112+
"code": 95042
4113+
},
4114+
"Annotate everything with types from JSDoc": {
4115+
"category": "Message",
4116+
"code": 95043
4117+
},
4118+
"Add '()' to all uncalled decorators": {
4119+
"category": "Message",
4120+
"code": 95044
4121+
},
4122+
"Convert all constructor functions to classes": {
4123+
"category": "Message",
4124+
"code": 95045
40294125
}
40304126
}

src/harness/fourslash.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -2457,12 +2457,14 @@ Actual: ${stringify(fullActual)}`);
24572457
this.verifyRangeIs(expectedText, includeWhiteSpace);
24582458
}
24592459

2460-
public verifyCodeFixAll(options: FourSlashInterface.VerifyCodeFixAllOptions): void {
2461-
const { fixId, newFileContent } = options;
2462-
const fixIds = ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId);
2463-
ts.Debug.assert(ts.contains(fixIds, fixId), "No available code fix has that group id.", () => `Expected '${fixId}'. Available action ids: ${fixIds}`);
2460+
public verifyCodeFixAll({ fixId, fixAllDescription, newFileContent, commands: expectedCommands }: FourSlashInterface.VerifyCodeFixAllOptions): void {
2461+
const fixWithId = ts.find(this.getCodeFixes(this.activeFile.fileName), a => a.fixId === fixId);
2462+
ts.Debug.assert(fixWithId !== undefined, "No available code fix has that group id.", () =>
2463+
`Expected '${fixId}'. Available action ids: ${ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId)}`);
2464+
ts.Debug.assertEqual(fixWithId.fixAllDescription, fixAllDescription);
2465+
24642466
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences);
2465-
assert.deepEqual(commands, options.commands);
2467+
assert.deepEqual(commands, expectedCommands);
24662468
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
24672469
this.applyChanges(changes);
24682470
this.verifyCurrentFileContent(newFileContent);
@@ -4661,6 +4663,7 @@ namespace FourSlashInterface {
46614663

46624664
export interface VerifyCodeFixAllOptions {
46634665
fixId: string;
4666+
fixAllDescription: string;
46644667
newFileContent: string;
46654668
commands: ReadonlyArray<{}>;
46664669
}

src/server/client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ namespace ts.server {
557557
const request = this.processRequest<protocol.CodeFixRequest>(CommandNames.GetCodeFixes, args);
558558
const response = this.processResponse<protocol.CodeFixResponse>(request);
559559

560-
return response.body.map(({ description, changes, fixId }) => ({ description, changes: this.convertChanges(changes, file), fixId }));
560+
return response.body.map(({ description, changes, fixId, fixAllDescription }) => ({ description, changes: this.convertChanges(changes, file), fixId, fixAllDescription }));
561561
}
562562

563563
getCombinedCodeFix = notImplemented;

src/server/protocol.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,8 @@ namespace ts.server.protocol {
17011701
* This may be omitted to indicate that the code fix can't be applied in a group.
17021702
*/
17031703
fixId?: {};
1704+
/** Should be present if and only if 'fixId' is. */
1705+
fixAllDescription?: string;
17041706
}
17051707

17061708
/**

src/server/session.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1723,9 +1723,9 @@ namespace ts.server {
17231723
return { startPosition, endPosition };
17241724
}
17251725

1726-
private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands, fixId }: CodeFixAction): protocol.CodeFixAction {
1726+
private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands, fixId, fixAllDescription }: CodeFixAction): protocol.CodeFixAction {
17271727
const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, project.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName))));
1728-
return { description, changes, commands, fixId };
1728+
return { description, changes, commands, fixId, fixAllDescription };
17291729
}
17301730

17311731
private mapTextChangesToCodeEdits(project: Project, textChanges: ReadonlyArray<FileTextChanges>): protocol.FileCodeEdits[] {

src/services/codeFixProvider.ts

+19
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ namespace ts {
2727
const codeFixRegistrations: CodeFixRegistration[][] = [];
2828
const fixIdToRegistration = createMap<CodeFixRegistration>();
2929

30+
type DiagnosticAndArguments = DiagnosticMessage | [DiagnosticMessage, string] | [DiagnosticMessage, string, string];
31+
function diagnosticToString(diag: DiagnosticAndArguments): string {
32+
return isArray(diag)
33+
? formatStringFromArgs(getLocaleSpecificMessage(diag[0]), diag.slice(1) as ReadonlyArray<string>)
34+
: getLocaleSpecificMessage(diag);
35+
}
36+
37+
export function createCodeFixActionNoFixId(changes: FileTextChanges[], description: DiagnosticAndArguments) {
38+
return createCodeFixActionWorker(diagnosticToString(description), changes, /*fixId*/ undefined, /*fixAllDescription*/ undefined);
39+
}
40+
41+
export function createCodeFixAction(changes: FileTextChanges[], description: DiagnosticAndArguments, fixId: {}, fixAllDescription: DiagnosticAndArguments, command?: CodeActionCommand): CodeFixAction {
42+
return createCodeFixActionWorker(diagnosticToString(description), changes, fixId, diagnosticToString(fixAllDescription), command);
43+
}
44+
45+
function createCodeFixActionWorker(description: string, changes: FileTextChanges[], fixId?: {}, fixAllDescription?: string, command?: CodeActionCommand): CodeFixAction {
46+
return { description, changes, fixId, fixAllDescription, commands: command ? [command] : undefined };
47+
}
48+
3049
export function registerCodeFix(reg: CodeFixRegistration) {
3150
for (const error of reg.errorCodes) {
3251
let registrations = codeFixRegistrations[error];

src/services/codefixes/addMissingInvocationForDecorator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace ts.codefix {
66
errorCodes,
77
getCodeActions: (context) => {
88
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start));
9-
return [{ description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression), changes, fixId }];
9+
return [createCodeFixAction(changes, Diagnostics.Call_decorator_expression, fixId, Diagnostics.Add_to_all_uncalled_decorators)];
1010
},
1111
fixIds: [fixId],
1212
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file!, diag.start!)),

src/services/codefixes/annotateWithTypeFromJSDoc.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ namespace ts.codefix {
77
getCodeActions(context) {
88
const decl = getDeclaration(context.sourceFile, context.span.start);
99
if (!decl) return;
10-
const description = getLocaleSpecificMessage(Diagnostics.Annotate_with_type_from_JSDoc);
1110
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, decl));
12-
return [{ description, changes, fixId }];
11+
return [createCodeFixAction(changes, Diagnostics.Annotate_with_type_from_JSDoc, fixId, Diagnostics.Annotate_everything_with_types_from_JSDoc)];
1312
},
1413
fixIds: [fixId],
1514
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {

src/services/codefixes/convertFunctionToEs6Class.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace ts.codefix {
66
errorCodes,
77
getCodeActions(context: CodeFixContext) {
88
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.program.getTypeChecker()));
9-
return [{ description: getLocaleSpecificMessage(Diagnostics.Convert_function_to_an_ES2015_class), changes, fixId }];
9+
return [createCodeFixAction(changes, Diagnostics.Convert_function_to_an_ES2015_class, fixId, Diagnostics.Convert_all_constructor_functions_to_classes)];
1010
},
1111
fixIds: [fixId],
1212
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, err) => doChange(changes, err.file!, err.start, context.program.getTypeChecker())),

src/services/codefixes/convertToEs6Module.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ namespace ts.codefix {
33
registerCodeFix({
44
errorCodes: [Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module.code],
55
getCodeActions(context) {
6-
const description = getLocaleSpecificMessage(Diagnostics.Convert_to_ES6_module);
76
const { sourceFile, program } = context;
87
const changes = textChanges.ChangeTracker.with(context, changes => {
98
const moduleExportsChangedToDefault = convertFileToEs6Module(sourceFile, program.getTypeChecker(), changes, program.getCompilerOptions().target);
@@ -14,7 +13,7 @@ namespace ts.codefix {
1413
}
1514
});
1615
// No support for fix-all since this applies to the whole file at once anyway.
17-
return [{ description, changes, fixId: undefined }];
16+
return [createCodeFixActionNoFixId(changes, Diagnostics.Convert_to_ES6_module)];
1817
},
1918
});
2019

src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ namespace ts.codefix {
88
const qualifiedName = getQualifiedName(context.sourceFile, context.span.start);
99
if (!qualifiedName) return undefined;
1010
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName));
11-
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${qualifiedName.left.text}["${qualifiedName.right.text}"]`]);
12-
return [{ description, changes, fixId }];
11+
const newText = `${qualifiedName.left.text}["${qualifiedName.right.text}"]`;
12+
return [createCodeFixAction(changes, [Diagnostics.Rewrite_as_the_indexed_access_type_0, newText], fixId, Diagnostics.Rewrite_all_as_indexed_access_types)];
1313
},
1414
fixIds: [fixId],
1515
getAllCodeActions: (context) => codeFixAll(context, errorCodes, (changes, diag) => {

src/services/codefixes/disableJsDiagnostics.ts

+6-11
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,18 @@ namespace ts.codefix {
1616
}
1717

1818
const fixes: CodeFixAction[] = [
19-
{
20-
description: getLocaleSpecificMessage(Diagnostics.Disable_checking_for_this_file),
21-
changes: [createFileTextChanges(sourceFile.fileName, [
19+
// fixId unnecessary because adding `// @ts-nocheck` even once will ignore every error in the file.
20+
createCodeFixActionNoFixId(
21+
[createFileTextChanges(sourceFile.fileName, [
2222
createTextChange(sourceFile.checkJsDirective
2323
? createTextSpanFromBounds(sourceFile.checkJsDirective.pos, sourceFile.checkJsDirective.end)
2424
: createTextSpan(0, 0), `// @ts-nocheck${getNewLineOrDefaultFromHost(host, formatContext.options)}`),
2525
])],
26-
// fixId unnecessary because adding `// @ts-nocheck` even once will ignore every error in the file.
27-
fixId: undefined,
28-
}];
26+
Diagnostics.Disable_checking_for_this_file),
27+
];
2928

3029
if (textChanges.isValidLocationToAddComment(sourceFile, span.start)) {
31-
fixes.unshift({
32-
description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message),
33-
changes: textChanges.ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)),
34-
fixId,
35-
});
30+
fixes.unshift(createCodeFixAction(textChanges.ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)), Diagnostics.Ignore_this_error_message, fixId, Diagnostics.Add_ts_ignore_to_all_error_messages));
3631
}
3732

3833
return fixes;

src/services/codefixes/fixAddMissingMember.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,8 @@ namespace ts.codefix {
9797

9898
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
9999
const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic));
100-
if (changes.length === 0) return undefined;
101-
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]);
102-
return { description, changes, fixId };
100+
return changes.length === 0 ? undefined
101+
: createCodeFixAction(changes, [makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor, tokenName], fixId, Diagnostics.Add_all_missing_members);
103102
}
104103

105104
function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): void {
@@ -143,9 +142,8 @@ namespace ts.codefix {
143142
}
144143

145144
function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction {
146-
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]);
147145
const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic));
148-
return { description, changes, fixId };
146+
return createCodeFixAction(changes, [makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, tokenName], fixId, Diagnostics.Add_all_missing_members);
149147
}
150148

151149
function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean): void {
@@ -178,7 +176,7 @@ namespace ts.codefix {
178176

179177
const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature));
180178
// No fixId here because code-fix-all currently only works on adding individual named properties.
181-
return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined };
179+
return createCodeFixActionNoFixId(changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]);
182180
}
183181

184182
function getActionForMethodDeclaration(
@@ -191,9 +189,8 @@ namespace ts.codefix {
191189
inJs: boolean,
192190
preferences: UserPreferences,
193191
): CodeFixAction | undefined {
194-
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]);
195192
const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs, preferences));
196-
return { description, changes, fixId };
193+
return createCodeFixAction(changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members);
197194
}
198195

199196
function addMethodDeclaration(

src/services/codefixes/fixAwaitInSyncFunction.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace ts.codefix {
1212
const nodes = getNodes(sourceFile, span.start);
1313
if (!nodes) return undefined;
1414
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes));
15-
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }];
15+
return [createCodeFixAction(changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers)];
1616
},
1717
fixIds: [fixId],
1818
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {

0 commit comments

Comments
 (0)