Skip to content

Commit 74b4540

Browse files
committed
Prioritize implement interface over remove unused in JS/ts
Fixes microsoft#94212 If both `implement interface` and `remove unused` are possible, only mark `implement interface` as prefered. Also changes our core code action sorting logic to prioritize autofixes
1 parent 7c49df6 commit 74b4540

2 files changed

Lines changed: 62 additions & 23 deletions

File tree

extensions/typescript-language-features/src/features/quickFix.ts

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,29 @@ class DiagnosticsSet {
126126
}
127127
}
128128

129+
class VsCodeCodeAction extends vscode.CodeAction {
130+
constructor(
131+
public readonly tsAction: Proto.CodeFixAction,
132+
title: string,
133+
kind: vscode.CodeActionKind,
134+
) {
135+
super(title, kind);
136+
}
137+
}
138+
129139
class CodeActionSet {
130-
private readonly _actions = new Set<vscode.CodeAction>();
131-
private readonly _fixAllActions = new Map<{}, vscode.CodeAction>();
140+
private readonly _actions = new Set<VsCodeCodeAction>();
141+
private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>();
132142

133-
public get values(): Iterable<vscode.CodeAction> {
143+
public get values(): Iterable<VsCodeCodeAction> {
134144
return this._actions;
135145
}
136146

137-
public addAction(action: vscode.CodeAction) {
147+
public addAction(action: VsCodeCodeAction) {
138148
this._actions.add(action);
139149
}
140150

141-
public addFixAllAction(fixId: {}, action: vscode.CodeAction) {
151+
public addFixAllAction(fixId: {}, action: VsCodeCodeAction) {
142152
const existing = this._fixAllActions.get(fixId);
143153
if (existing) {
144154
// reinsert action at back of actions list
@@ -219,7 +229,13 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider {
219229
for (const diagnostic of fixableDiagnostics.values) {
220230
await this.getFixesForDiagnostic(document, file, diagnostic, results, token);
221231
}
222-
return Array.from(results.values);
232+
233+
const allActions = Array.from(results.values);
234+
const allTsActions = allActions.map(x => x.tsAction);
235+
for (const action of allActions) {
236+
action.isPreferred = isPreferredFix(action.tsAction, allTsActions);
237+
}
238+
return allActions;
223239
}
224240

225241
private async getFixesForDiagnostic(
@@ -259,16 +275,15 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider {
259275
private getSingleFixForTsCodeAction(
260276
diagnostic: vscode.Diagnostic,
261277
tsAction: Proto.CodeFixAction
262-
): vscode.CodeAction {
263-
const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix);
278+
): VsCodeCodeAction {
279+
const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix);
264280
codeAction.edit = getEditForCodeAction(this.client, tsAction);
265281
codeAction.diagnostics = [diagnostic];
266282
codeAction.command = {
267283
command: ApplyCodeActionCommand.ID,
268284
arguments: [tsAction],
269285
title: ''
270286
};
271-
codeAction.isPreferred = isPreferredFix(tsAction);
272287
return codeAction;
273288
}
274289

@@ -294,7 +309,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider {
294309
return results;
295310
}
296311

297-
const action = new vscode.CodeAction(
312+
const action = new VsCodeCodeAction(
313+
tsAction,
298314
tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description),
299315
vscode.CodeActionKind.QuickFix);
300316
action.diagnostics = [diagnostic];
@@ -317,20 +333,37 @@ const fixAllErrorCodes = new Map<number, number>([
317333
]);
318334

319335

320-
const preferredFixes = new Set([
321-
'annotateWithTypeFromJSDoc',
322-
'constructorForDerivedNeedSuperCall',
323-
'extendsInterfaceBecomesImplements',
324-
'fixAwaitInSyncFunction',
325-
'fixClassIncorrectlyImplementsInterface',
326-
'fixUnreachableCode',
327-
'forgottenThisPropertyAccess',
328-
'spelling',
329-
'unusedIdentifier',
330-
'addMissingAwait',
336+
const preferredFixes = new Map<string, /* priorty */number>([
337+
['annotateWithTypeFromJSDoc', 0],
338+
['constructorForDerivedNeedSuperCall', 0],
339+
['extendsInterfaceBecomesImplements', 0],
340+
['fixAwaitInSyncFunction', 0],
341+
['fixClassIncorrectlyImplementsInterface', 1],
342+
['fixUnreachableCode', 0],
343+
['unusedIdentifier', 0],
344+
['forgottenThisPropertyAccess', 0],
345+
['spelling', 1],
346+
['addMissingAwait', 0],
331347
]);
332-
function isPreferredFix(tsAction: Proto.CodeFixAction): boolean {
333-
return preferredFixes.has(tsAction.fixName);
348+
349+
function isPreferredFix(
350+
tsAction: Proto.CodeFixAction,
351+
allActions: readonly Proto.CodeFixAction[]
352+
): boolean {
353+
const priority = preferredFixes.get(tsAction.fixName);
354+
if (typeof priority === 'undefined') {
355+
return false;
356+
}
357+
return allActions.every(otherAction => {
358+
if (otherAction === tsAction) {
359+
return true;
360+
}
361+
const otherPriority = preferredFixes.get(otherAction.fixName);
362+
if (typeof otherPriority === 'undefined') {
363+
return true;
364+
}
365+
return priority >= otherPriority;
366+
});
334367
}
335368

336369
export function register(

src/vs/editor/contrib/codeAction/codeAction.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ export interface CodeActionSet extends IDisposable {
3535
class ManagedCodeActionSet extends Disposable implements CodeActionSet {
3636

3737
private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): number {
38+
if (a.isPreferred && !b.isPreferred) {
39+
return -1;
40+
} else if (!a.isPreferred && b.isPreferred) {
41+
return 1;
42+
}
43+
3844
if (isNonEmptyArray(a.diagnostics)) {
3945
if (isNonEmptyArray(b.diagnostics)) {
4046
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);

0 commit comments

Comments
 (0)