Skip to content

Commit 1a55beb

Browse files
authored
Reduce duplication around declaring code action groups (microsoft#160741)
Declaring a new code action group is currently tricky as it requires updating two different locations in the code This changes fixes that by introducing a top level list of codeActionGroups. To add a new menu group, you just need to update this list
1 parent 12d0b63 commit 1a55beb

File tree

1 file changed

+33
-49
lines changed

1 file changed

+33
-49
lines changed

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

+33-49
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ enum CodeActionListItemKind {
5555
interface CodeActionListItemCodeAction {
5656
readonly kind: CodeActionListItemKind.CodeAction;
5757
readonly action: CodeActionItem;
58+
readonly group: CodeActionGroup;
5859
}
5960

6061
interface CodeActionListItemHeader {
6162
readonly kind: CodeActionListItemKind.Header;
62-
readonly headerTitle: string;
63+
readonly group: CodeActionGroup;
6364
}
6465

6566
type ICodeActionMenuItem = CodeActionListItemCodeAction | CodeActionListItemHeader;
@@ -75,6 +76,24 @@ function stripNewlines(str: string): string {
7576
return str.replace(/\r\n|\r|\n/g, ' ');
7677
}
7778

79+
interface CodeActionGroup {
80+
readonly kind: CodeActionKind;
81+
readonly title: string;
82+
readonly icon: Codicon;
83+
readonly iconColor?: string;
84+
}
85+
86+
const uncategorizedCodeActionGroup = Object.freeze<CodeActionGroup>({ kind: CodeActionKind.Empty, title: localize('codeAction.widget.id.more', 'More Actions...'), icon: Codicon.lightBulb, iconColor: 'var(--vscode-editorLightBulb-foreground)' });
87+
88+
const codeActionGroups = Object.freeze<CodeActionGroup[]>([
89+
{ kind: CodeActionKind.QuickFix, title: localize('codeAction.widget.id.quickfix', 'Quick Fix...'), icon: Codicon.lightBulb, },
90+
{ kind: CodeActionKind.Extract, title: localize('codeAction.widget.id.extract', 'Extract...'), icon: Codicon.wrench, },
91+
{ kind: CodeActionKind.Convert, title: localize('codeAction.widget.id.convert', 'Convert...'), icon: Codicon.zap, iconColor: 'var(--vscode-editorLightBulbAutoFix-foreground)' },
92+
{ kind: CodeActionKind.SurroundWith, title: localize('codeAction.widget.id.surround', 'Surround With...'), icon: Codicon.symbolArray, },
93+
{ kind: CodeActionKind.Source, title: localize('codeAction.widget.id.source', 'Source Action...'), icon: Codicon.lightBulb, iconColor: 'var(--vscode-editorLightBulb-foreground)' },
94+
uncategorizedCodeActionGroup,
95+
]);
96+
7897
class CodeActionItemRenderer implements IListRenderer<CodeActionListItemCodeAction, ICodeActionMenuTemplateData> {
7998
constructor(
8099
private readonly keybindingResolver: CodeActionKeybindingResolver,
@@ -100,22 +119,8 @@ class CodeActionItemRenderer implements IListRenderer<CodeActionListItemCodeActi
100119
}
101120

102121
renderElement(element: CodeActionListItemCodeAction, _index: number, data: ICodeActionMenuTemplateData): void {
103-
// Icons and Label modification based on group
104-
const kind = element.action.action.kind ? new CodeActionKind(element.action.action.kind) : CodeActionKind.None;
105-
if (CodeActionKind.SurroundWith.contains(kind)) {
106-
data.icon.className = Codicon.symbolArray.classNames;
107-
} else if (CodeActionKind.Extract.contains(kind)) {
108-
data.icon.className = Codicon.wrench.classNames;
109-
} else if (CodeActionKind.Convert.contains(kind)) {
110-
data.icon.className = Codicon.zap.classNames;
111-
data.icon.style.color = `var(--vscode-editorLightBulbAutoFix-foreground)`;
112-
} else if (CodeActionKind.QuickFix.contains(kind)) {
113-
data.icon.className = Codicon.lightBulb.classNames;
114-
data.icon.style.color = `var(--vscode-editorLightBulb-foreground)`;
115-
} else {
116-
data.icon.className = Codicon.lightBulb.classNames;
117-
data.icon.style.color = `var(--vscode-editorLightBulb-foreground)`;
118-
}
122+
data.icon.className = element.group.icon.classNames;
123+
data.icon.style.color = element.group.iconColor ?? '';
119124

120125
data.text.textContent = stripNewlines(element.action.action.title);
121126

@@ -160,7 +165,7 @@ class HeaderRenderer implements IListRenderer<CodeActionListItemHeader, HeaderTe
160165
}
161166

162167
renderElement(element: CodeActionListItemHeader, _index: number, templateData: HeaderTemplateData): void {
163-
templateData.text.textContent = element.headerTitle;
168+
templateData.text.textContent = element.group.title;
164169
}
165170

166171
disposeTemplate(_templateData: HeaderTemplateData): void {
@@ -303,49 +308,28 @@ class CodeActionList extends Disposable {
303308

304309
private toMenuItems(inputCodeActions: readonly CodeActionItem[], showHeaders: boolean): ICodeActionMenuItem[] {
305310
if (!showHeaders) {
306-
return inputCodeActions.map((action): ICodeActionMenuItem => ({ kind: CodeActionListItemKind.CodeAction, action }));
311+
return inputCodeActions.map((action): ICodeActionMenuItem => ({ kind: CodeActionListItemKind.CodeAction, action, group: uncategorizedCodeActionGroup }));
307312
}
308313

309-
// Groups code actions by their kind
310-
const quickfixGroup: CodeActionItem[] = [];
311-
const extractGroup: CodeActionItem[] = [];
312-
const convertGroup: CodeActionItem[] = [];
313-
const surroundGroup: CodeActionItem[] = [];
314-
const sourceGroup: CodeActionItem[] = [];
315-
const otherGroup: CodeActionItem[] = [];
314+
// Group code actions
315+
const menuEntries = codeActionGroups.map(group => ({ group, actions: [] as CodeActionItem[] }));
316316

317317
for (const action of inputCodeActions) {
318318
const kind = action.action.kind ? new CodeActionKind(action.action.kind) : CodeActionKind.None;
319-
if (CodeActionKind.SurroundWith.contains(kind)) {
320-
surroundGroup.push(action);
321-
} else if (CodeActionKind.QuickFix.contains(kind)) {
322-
quickfixGroup.push(action);
323-
} else if (CodeActionKind.Extract.contains(kind)) {
324-
extractGroup.push(action);
325-
} else if (CodeActionKind.Convert.contains(kind)) {
326-
convertGroup.push(action);
327-
} else if (CodeActionKind.Source.contains(kind)) {
328-
sourceGroup.push(action);
329-
} else {
330-
otherGroup.push(action);
319+
for (const menuEntry of menuEntries) {
320+
if (menuEntry.group.kind.contains(kind)) {
321+
menuEntry.actions.push(action);
322+
break;
323+
}
331324
}
332325
}
333326

334-
const menuEntries: ReadonlyArray<{ title: string; actions: CodeActionItem[] }> = [
335-
{ title: localize('codeAction.widget.id.quickfix', 'Quick Fix...'), actions: quickfixGroup },
336-
{ title: localize('codeAction.widget.id.extract', 'Extract...'), actions: extractGroup },
337-
{ title: localize('codeAction.widget.id.convert', 'Convert...'), actions: convertGroup },
338-
{ title: localize('codeAction.widget.id.surround', 'Surround With...'), actions: surroundGroup },
339-
{ title: localize('codeAction.widget.id.source', 'Source Action...'), actions: sourceGroup },
340-
{ title: localize('codeAction.widget.id.more', 'More Actions...'), actions: otherGroup },
341-
];
342-
343327
const allMenuItems: ICodeActionMenuItem[] = [];
344328
for (const menuEntry of menuEntries) {
345329
if (menuEntry.actions.length) {
346-
allMenuItems.push({ kind: CodeActionListItemKind.Header, headerTitle: menuEntry.title });
330+
allMenuItems.push({ kind: CodeActionListItemKind.Header, group: menuEntry.group });
347331
for (const action of menuEntry.actions) {
348-
allMenuItems.push({ kind: CodeActionListItemKind.CodeAction, action });
332+
allMenuItems.push({ kind: CodeActionListItemKind.CodeAction, action, group: menuEntry.group });
349333
}
350334
}
351335
}

0 commit comments

Comments
 (0)