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'; @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 { 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 { 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() 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 label = selectedProgrammer ? `Programmer: "${selectedProgrammer.name}"` : 'Programmer' 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(); } } }); } }