import { CommandHandler, CommandRegistry, } from '@theia/core/lib/common/command'; import { Disposable, DisposableCollection, } from '@theia/core/lib/common/disposable'; import { MenuModelRegistry, MenuPath, SubMenuOptions, } from '@theia/core/lib/common/menu'; import { unregisterSubmenu } from './arduino-menus'; export interface MenuTemplate { readonly menuLabel: string; } export function isMenuTemplate(arg: unknown): arg is MenuTemplate { return ( typeof arg === 'object' && (arg as MenuTemplate).menuLabel !== undefined && typeof (arg as MenuTemplate).menuLabel === 'string' ); } export interface MenuActionTemplate extends MenuTemplate { readonly menuPath: MenuPath; readonly handler: CommandHandler; /** * If not defined the insertion oder will be the order string. */ readonly order?: string; } export function isMenuActionTemplate( arg: MenuTemplate ): arg is MenuActionTemplate { return ( isMenuTemplate(arg) && (arg as MenuActionTemplate).handler !== undefined && typeof (arg as MenuActionTemplate).handler === 'object' && (arg as MenuActionTemplate).menuPath !== undefined && Array.isArray((arg as MenuActionTemplate).menuPath) ); } export function menuActionWithCommandDelegate( template: Omit<MenuActionTemplate, 'handler' | 'menuLabel'> & { command: string; }, commandRegistry: CommandRegistry ): MenuActionTemplate { const id = template.command; const command = commandRegistry.getCommand(id); if (!command) { throw new Error(`Could not find the registered command with ID: ${id}`); } return { ...template, menuLabel: command.label ?? id, handler: { execute: (args) => commandRegistry.executeCommand(id, args), isEnabled: (args) => commandRegistry.isEnabled(id, args), isVisible: (args) => commandRegistry.isVisible(id, args), isToggled: (args) => commandRegistry.isToggled(id, args), }, }; } export interface SubmenuTemplate extends MenuTemplate { readonly menuLabel: string; readonly submenuPath: MenuPath; readonly options?: SubMenuOptions; } interface Services { readonly commandRegistry: CommandRegistry; readonly menuRegistry: MenuModelRegistry; } class MenuIndexCounter { private _counter: number; constructor(counter = 0) { this._counter = counter; } getAndIncrement(): number { const counter = this._counter; this._counter++; return counter; } } export function registerMenus( options: { contextId: string; templates: Array<MenuActionTemplate | SubmenuTemplate>; } & Services ): Disposable { const { templates } = options; const menuIndexCounter = new MenuIndexCounter(); return new DisposableCollection( ...templates.map((template) => registerMenu({ template, menuIndexCounter, ...options }) ) ); } function registerMenu( options: { contextId: string; menuIndexCounter: MenuIndexCounter; template: MenuActionTemplate | SubmenuTemplate; } & Services ): Disposable { const { template, commandRegistry, menuRegistry, contextId, menuIndexCounter, } = options; if (isMenuActionTemplate(template)) { const { menuLabel, menuPath, handler, order } = template; const id = generateCommandId(contextId, menuLabel, menuPath); const index = menuIndexCounter.getAndIncrement(); return new DisposableCollection( commandRegistry.registerCommand({ id }, handler), menuRegistry.registerMenuAction(menuPath, { commandId: id, label: menuLabel, order: typeof order === 'string' ? order : String(index).padStart(4), }) ); } else { const { menuLabel, submenuPath, options } = template; return new DisposableCollection( menuRegistry.registerSubmenu(submenuPath, menuLabel, options), Disposable.create(() => unregisterSubmenu(submenuPath, menuRegistry)) ); } function generateCommandId( contextId: string, menuLabel: string, menuPath: MenuPath ): string { return `arduino-${contextId}-context-${menuPath.join('-')}-${menuLabel}`; } }