mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-23 20:46:33 +00:00
152 lines
6.1 KiB
TypeScript
152 lines
6.1 KiB
TypeScript
import * as PQueue from 'p-queue';
|
|
import { inject, injectable, postConstruct } from 'inversify';
|
|
import { MenuPath, SubMenuOptions, CompositeMenuNode } from '@theia/core/lib/common/menu';
|
|
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
import { OpenSketch } from './open-sketch';
|
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
|
import { MainMenuManager } from '../../common/main-menu-manager';
|
|
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
|
import { ExamplesService, ExampleContainer } from '../../common/protocol/examples-service';
|
|
import { SketchContribution, CommandRegistry, MenuModelRegistry } from './contribution';
|
|
import { NotificationCenter } from '../notification-center';
|
|
|
|
@injectable()
|
|
export abstract class Examples extends SketchContribution {
|
|
|
|
@inject(CommandRegistry)
|
|
protected readonly commandRegistry: CommandRegistry;
|
|
|
|
@inject(MenuModelRegistry)
|
|
protected readonly menuRegistry: MenuModelRegistry;
|
|
|
|
@inject(MainMenuManager)
|
|
protected readonly menuManager: MainMenuManager;
|
|
|
|
@inject(ExamplesService)
|
|
protected readonly examplesService: ExamplesService;
|
|
|
|
@inject(BoardsServiceProvider)
|
|
protected readonly boardsServiceClient: BoardsServiceProvider;
|
|
|
|
protected readonly toDispose = new DisposableCollection();
|
|
|
|
@postConstruct()
|
|
init(): void {
|
|
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.handleBoardChanged(selectedBoard?.fqbn));
|
|
}
|
|
|
|
protected handleBoardChanged(fqbn: string | undefined): void {
|
|
// NOOP
|
|
}
|
|
|
|
registerMenus(registry: MenuModelRegistry): void {
|
|
try {
|
|
// This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222.
|
|
const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1;
|
|
const menuId = ArduinoMenus.FILE__EXAMPLES_SUBMENU[index];
|
|
const groupPath = index === 0 ? [] : ArduinoMenus.FILE__EXAMPLES_SUBMENU.slice(0, index);
|
|
const parent: CompositeMenuNode = (registry as any).findGroup(groupPath);
|
|
const examples = new CompositeMenuNode(menuId, '', { order: '4' });
|
|
parent.addNode(examples);
|
|
} catch (e) {
|
|
console.error(e);
|
|
console.warn('Could not patch menu ordering.');
|
|
}
|
|
// Registering the same submenu multiple times has no side-effect.
|
|
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
|
registry.registerSubmenu(ArduinoMenus.FILE__EXAMPLES_SUBMENU, 'Examples', { order: '4' });
|
|
}
|
|
|
|
registerRecursively(
|
|
exampleContainer: ExampleContainer,
|
|
menuPath: MenuPath,
|
|
pushToDispose: DisposableCollection = new DisposableCollection(),
|
|
options?: SubMenuOptions): void {
|
|
|
|
const { label, sketches, children } = exampleContainer;
|
|
const submenuPath = [...menuPath, label];
|
|
this.menuRegistry.registerSubmenu(submenuPath, label, options);
|
|
children.forEach(child => this.registerRecursively(child, submenuPath, pushToDispose));
|
|
for (const sketch of sketches) {
|
|
const { uri } = sketch;
|
|
const commandId = `arduino-open-example-${submenuPath.join(':')}--${uri}`;
|
|
const command = { id: commandId };
|
|
const handler = {
|
|
execute: async () => {
|
|
const sketch = await this.sketchService.cloneExample(uri);
|
|
this.commandService.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch);
|
|
}
|
|
};
|
|
pushToDispose.push(this.commandRegistry.registerCommand(command, handler));
|
|
this.menuRegistry.registerMenuAction(submenuPath, { commandId, label: sketch.name });
|
|
pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(command)));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
@injectable()
|
|
export class BuiltInExamples extends Examples {
|
|
|
|
onStart(): void {
|
|
this.register(); // no `await`
|
|
}
|
|
|
|
protected async register() {
|
|
let exampleContainers: ExampleContainer[] | undefined;
|
|
try {
|
|
exampleContainers = await this.examplesService.builtIns();
|
|
} catch (e) {
|
|
console.error('Could not initialize built-in examples.', e);
|
|
this.messageService.error('Could not initialize built-in examples.');
|
|
return;
|
|
}
|
|
this.toDispose.dispose();
|
|
for (const container of exampleContainers) {
|
|
this.registerRecursively(container, ArduinoMenus.EXAMPLES__BUILT_IN_GROUP, this.toDispose);
|
|
}
|
|
this.menuManager.update();
|
|
}
|
|
|
|
}
|
|
|
|
@injectable()
|
|
export class LibraryExamples extends Examples {
|
|
|
|
@inject(NotificationCenter)
|
|
protected readonly notificationCenter: NotificationCenter;
|
|
|
|
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
|
|
|
onStart(): void {
|
|
this.register(); // no `await`
|
|
this.notificationCenter.onLibraryInstalled(() => this.register());
|
|
this.notificationCenter.onLibraryUninstalled(() => this.register());
|
|
}
|
|
|
|
protected handleBoardChanged(fqbn: string | undefined): void {
|
|
this.register(fqbn);
|
|
}
|
|
|
|
protected async register(fqbn: string | undefined = this.boardsServiceClient.boardsConfig.selectedBoard?.fqbn) {
|
|
return this.queue.add(async () => {
|
|
this.toDispose.dispose();
|
|
if (!fqbn) {
|
|
return;
|
|
}
|
|
const { user, current, any } = await this.examplesService.installed({ fqbn });
|
|
for (const container of user) {
|
|
this.registerRecursively(container, ArduinoMenus.EXAMPLES__USER_LIBS_GROUP, this.toDispose);
|
|
}
|
|
for (const container of current) {
|
|
this.registerRecursively(container, ArduinoMenus.EXAMPLES__CURRENT_BOARD_GROUP, this.toDispose);
|
|
}
|
|
for (const container of any) {
|
|
this.registerRecursively(container, ArduinoMenus.EXAMPLES__ANY_BOARD_GROUP, this.toDispose);
|
|
}
|
|
this.menuManager.update();
|
|
});
|
|
}
|
|
|
|
}
|