import * as PQueue from 'p-queue'; import { inject, injectable } from 'inversify'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { Disposable, DisposableCollection, } from '@theia/core/lib/common/disposable'; import { BoardsServiceProvider } from './boards-service-provider'; import { Board, ConfigOption, Programmer } from '../../common/protocol'; import { FrontendApplicationContribution } from '@theia/core/lib/browser'; import { BoardsDataStore } from './boards-data-store'; import { MainMenuManager } from '../../common/main-menu-manager'; import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus'; import { nls } from '@theia/core/lib/common'; @injectable() export class BoardsDataMenuUpdater implements FrontendApplicationContribution { @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry; @inject(MainMenuManager) protected readonly mainMenuManager: MainMenuManager; @inject(BoardsDataStore) protected readonly boardsDataStore: BoardsDataStore; @inject(BoardsServiceProvider) protected readonly boardsServiceClient: BoardsServiceProvider; protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly toDisposeOnBoardChange = new DisposableCollection(); async onStart(): Promise<void> { this.updateMenuActions(this.boardsServiceClient.boardsConfig.selectedBoard); this.boardsDataStore.onChanged(() => this.updateMenuActions( this.boardsServiceClient.boardsConfig.selectedBoard ) ); this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.updateMenuActions(selectedBoard) ); } protected async updateMenuActions( selectedBoard: Board | undefined ): Promise<void> { return this.queue.add(async () => { this.toDisposeOnBoardChange.dispose(); this.mainMenuManager.update(); if (selectedBoard) { const { fqbn } = selectedBoard; if (fqbn) { const { configOptions, programmers, selectedProgrammer } = await this.boardsDataStore.getData(fqbn); if (configOptions.length) { const boardsConfigMenuPath = [ ...ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, 'z01_boardsConfig', ]; // `z_` is for ordering. for (const { label, option, values } of configOptions.sort( ConfigOption.LABEL_COMPARATOR )) { const menuPath = [...boardsConfigMenuPath, `${option}`]; const commands = new Map< string, Disposable & { label: string } >(); for (const value of values) { const id = `${fqbn}-${option}--${value.value}`; const command = { id }; const selectedValue = value.value; const handler = { execute: () => this.boardsDataStore.selectConfigOption({ fqbn, option, selectedValue, }), isToggled: () => value.selected, }; commands.set( id, Object.assign( this.commandRegistry.registerCommand(command, handler), { label: value.label } ) ); } this.menuRegistry.registerSubmenu(menuPath, label); this.toDisposeOnBoardChange.pushAll([ ...commands.values(), Disposable.create(() => unregisterSubmenu(menuPath, this.menuRegistry) ), ...Array.from(commands.keys()).map((commandId, i) => { const { label } = commands.get(commandId)!; this.menuRegistry.registerMenuAction(menuPath, { commandId, order: `${i}`, label, }); return Disposable.create(() => this.menuRegistry.unregisterMenuAction(commandId) ); }), ]); } } if (programmers.length) { const programmersMenuPath = [ ...ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, 'z02_programmers', ]; const programmerNls = nls.localize( 'arduino/board/programmer', 'Programmer' ); const label = selectedProgrammer ? `${programmerNls}: "${selectedProgrammer.name}"` : programmerNls; this.menuRegistry.registerSubmenu(programmersMenuPath, label); this.toDisposeOnBoardChange.push( Disposable.create(() => unregisterSubmenu(programmersMenuPath, this.menuRegistry) ) ); for (const programmer of programmers) { const { id, name } = programmer; const command = { id: `${fqbn}-programmer--${id}` }; const handler = { execute: () => this.boardsDataStore.selectProgrammer({ fqbn, selectedProgrammer: programmer, }), isToggled: () => Programmer.equals(programmer, selectedProgrammer), }; this.menuRegistry.registerMenuAction(programmersMenuPath, { commandId: command.id, label: name, }); this.commandRegistry.registerCommand(command, handler); this.toDisposeOnBoardChange.pushAll([ Disposable.create(() => this.commandRegistry.unregisterCommand(command) ), Disposable.create(() => this.menuRegistry.unregisterMenuAction(command.id) ), ]); } } this.mainMenuManager.update(); } } }); } }