mirror of
https://github.com/Microsoft/vscode
synced 2024-10-30 09:08:46 +00:00
prevent submenu cycles
This commit is contained in:
parent
5b2e4229ff
commit
c2e4add116
2 changed files with 21 additions and 7 deletions
|
@ -42,6 +42,7 @@ export interface IMenuOptions {
|
|||
anchorAlignment?: AnchorAlignment;
|
||||
expandDirection?: Direction;
|
||||
useEventAsContext?: boolean;
|
||||
submenuIds?: Set<string>;
|
||||
}
|
||||
|
||||
export interface IMenuStyles {
|
||||
|
@ -199,6 +200,15 @@ export class Menu extends ActionBar {
|
|||
|
||||
menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 30)}px`;
|
||||
|
||||
actions = actions.filter(a => {
|
||||
if (options.submenuIds?.has(a.id)) {
|
||||
console.warn(`Found submenu cycle: ${a.id}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
this.push(actions, { icon: true, label: true, isMenu: true });
|
||||
|
||||
container.appendChild(this.scrollableElement.getDomNode());
|
||||
|
@ -294,7 +304,7 @@ export class Menu extends ActionBar {
|
|||
return new MenuSeparatorActionViewItem(options.context, action, { icon: true });
|
||||
} else if (action instanceof SubmenuAction) {
|
||||
const actions = Array.isArray(action.actions) ? action.actions : action.actions();
|
||||
const menuActionViewItem = new SubmenuMenuActionViewItem(action, actions, parentData, options);
|
||||
const menuActionViewItem = new SubmenuMenuActionViewItem(action, actions, parentData, { ...options, submenuIds: new Set([...(options.submenuIds || []), action.id]) });
|
||||
|
||||
if (options.enableMnemonics) {
|
||||
const mnemonic = menuActionViewItem.getMnemonic();
|
||||
|
|
|
@ -25,6 +25,7 @@ import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contex
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { stripCodicons } from 'vs/base/common/codicons';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
|
||||
export class ContextMenuService extends Disposable implements IContextMenuService {
|
||||
|
||||
|
@ -123,14 +124,12 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
|||
}
|
||||
}
|
||||
|
||||
private createMenu(delegate: IContextMenuDelegate, entries: IAction[], onHide: () => void): IContextMenuItem[] {
|
||||
private createMenu(delegate: IContextMenuDelegate, entries: IAction[], onHide: () => void, submenuIds = new Set<string>()): IContextMenuItem[] {
|
||||
const actionRunner = delegate.actionRunner || new ActionRunner();
|
||||
|
||||
return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide));
|
||||
return coalesce(entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide, submenuIds)));
|
||||
}
|
||||
|
||||
private createMenuItem(delegate: IContextMenuDelegate, entry: IAction, actionRunner: IActionRunner, onHide: () => void): IContextMenuItem {
|
||||
|
||||
private createMenuItem(delegate: IContextMenuDelegate, entry: IAction, actionRunner: IActionRunner, onHide: () => void, submenuIds: Set<string>): IContextMenuItem | undefined {
|
||||
// Separator
|
||||
if (entry instanceof Separator) {
|
||||
return { type: 'separator' };
|
||||
|
@ -138,10 +137,15 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
|||
|
||||
// Submenu
|
||||
if (entry instanceof SubmenuAction) {
|
||||
if (submenuIds.has(entry.id)) {
|
||||
console.warn(`Found submenu cycle: ${entry.id}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const actions = Array.isArray(entry.actions) ? entry.actions : entry.actions();
|
||||
return {
|
||||
label: unmnemonicLabel(stripCodicons(entry.label)).trim(),
|
||||
submenu: this.createMenu(delegate, actions, onHide)
|
||||
submenu: this.createMenu(delegate, actions, onHide, new Set([...submenuIds, entry.id]))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue