From 0592199858ae4516347639063f1d90c970c49e62 Mon Sep 17 00:00:00 2001 From: Francesco Stasi Date: Wed, 16 Jun 2021 15:08:48 +0200 Subject: [PATCH] Use eslint&prettier for code linting&formatting --- .eslintrc.js | 49 + .prettierrc | 13 + .vscode/settings.json | 18 +- arduino-ide-extension/package.json | 2 +- .../src/browser/arduino-commands.ts | 8 +- .../browser/arduino-frontend-contribution.tsx | 319 ++++-- .../browser/arduino-ide-frontend-module.ts | 401 +++++-- .../src/browser/arduino-preferences.ts | 80 +- .../src/browser/arduino-workspace-resolver.ts | 29 +- .../browser/boards/boards-auto-installer.ts | 102 +- .../boards/boards-config-dialog-widget.tsx | 32 +- .../browser/boards/boards-config-dialog.ts | 43 +- .../src/browser/boards/boards-config.tsx | 354 ++++--- .../boards/boards-data-menu-updater.ts | 153 ++- .../src/browser/boards/boards-data-store.ts | 141 ++- .../src/browser/boards/boards-list-widget.ts | 50 +- .../browser/boards/boards-service-provider.ts | 348 +++++-- .../browser/boards/boards-toolbar-item.tsx | 182 ++-- .../boards-widget-frontend-contribution.ts | 6 +- .../src/browser/contributions/about.ts | 60 +- .../src/browser/contributions/add-file.ts | 27 +- .../browser/contributions/add-zip-library.ts | 62 +- .../browser/contributions/archive-sketch.ts | 35 +- .../browser/contributions/board-selection.ts | 228 +++- .../browser/contributions/burn-bootloader.ts | 38 +- .../src/browser/contributions/close.ts | 55 +- .../src/browser/contributions/contribution.ts | 83 +- .../src/browser/contributions/debug.ts | 76 +- .../contributions/edit-contributions.ts | 193 ++-- .../src/browser/contributions/examples.ts | 138 ++- .../src/browser/contributions/help.ts | 101 +- .../browser/contributions/include-library.ts | 112 +- .../src/browser/contributions/new-sketch.ts | 30 +- .../contributions/open-recent-sketch.ts | 58 +- .../contributions/open-sketch-external.ts | 18 +- .../src/browser/contributions/open-sketch.ts | 129 ++- .../src/browser/contributions/quit-app.ts | 20 +- .../browser/contributions/save-as-sketch.ts | 69 +- .../src/browser/contributions/save-sketch.ts | 29 +- .../src/browser/contributions/settings.ts | 23 +- .../browser/contributions/sketch-control.ts | 199 ++-- .../src/browser/contributions/sketchbook.ts | 27 +- .../browser/contributions/upload-sketch.ts | 74 +- .../browser/contributions/verify-sketch.ts | 50 +- .../src/browser/editor-mode.ts | 16 +- .../browser/library/library-list-widget.ts | 107 +- .../library-widget-frontend-contribution.ts | 13 +- .../src/browser/menu/arduino-menus.ts | 128 ++- .../src/browser/monitor/monitor-connection.ts | 170 ++- .../src/browser/monitor/monitor-model.ts | 59 +- .../monitor/monitor-service-client-impl.ts | 7 +- .../monitor/monitor-view-contribution.tsx | 103 +- .../src/browser/monitor/monitor-widget.tsx | 227 ++-- .../src/browser/notification-center.ts | 46 +- .../src/browser/response-service-impl.ts | 11 +- .../src/browser/settings.tsx | 852 +++++++++------ .../src/browser/storage-wrapper.ts | 18 +- .../browser/theia/core/application-shell.ts | 40 +- .../theia/core/browser-main-menu-factory.ts | 12 +- .../browser/theia/core/browser-menu-module.ts | 9 +- .../browser/theia/core/browser-menu-plugin.ts | 2 - .../core/common-frontend-contribution.ts | 9 +- .../theia/core/connection-status-service.ts | 36 +- .../theia/core/frontend-application.ts | 11 +- .../src/browser/theia/core/keybindings.ts | 14 +- .../theia/core/shell-layout-restorer.ts | 16 +- .../browser/theia/core/tab-bar-decorator.ts | 18 +- .../browser/theia/core/tab-bar-toolbar.tsx | 43 +- .../src/browser/theia/core/tab-bars.ts | 2 - .../debug/debug-configuration-manager.ts | 66 +- .../theia/debug/debug-configuration-model.ts | 30 +- .../browser/theia/debug/debug-editor-model.ts | 92 +- ...debug-frontend-application-contribution.ts | 9 +- .../browser/theia/debug/debug-hover-source.ts | 13 +- .../browser/theia/debug/debug-hover-widget.ts | 66 +- .../theia/debug/debug-session-manager.ts | 70 +- .../browser/theia/editor/editor-command.ts | 20 +- .../theia/editor/editor-contribution.ts | 7 +- .../theia/editor/editor-widget-factory.ts | 10 +- .../keymaps/keymaps-frontend-contribution.ts | 8 +- .../theia/markers/problem-contribution.ts | 4 +- .../browser/theia/markers/problem-manager.ts | 19 +- .../notification-center-component.tsx | 46 +- .../theia/messages/notification-component.tsx | 126 ++- .../notification-toasts-component.tsx | 20 +- .../theia/messages/notifications-manager.ts | 29 +- .../theia/messages/notifications-renderer.tsx | 16 +- .../theia/monaco/monaco-editor-provider.ts | 69 +- .../monaco/monaco-status-bar-contribution.ts | 8 +- .../theia/monaco/monaco-text-model-service.ts | 34 +- .../theia/navigator/navigator-contribution.ts | 29 +- .../navigator/navigator-tab-bar-decorator.ts | 2 - .../theia/outline/outline-contribution.ts | 5 +- .../browser/theia/output/output-channel.ts | 22 +- .../output/output-toolbar-contribution.ts | 15 +- .../src/browser/theia/output/output-widget.ts | 2 - .../output-channel-registry-main.ts | 18 +- .../preferences/preferences-contribution.ts | 7 +- .../src/browser/theia/scm/scm-contribution.ts | 2 - ...arch-in-workspace-frontend-contribution.ts | 15 +- .../search-in-workspace-result-tree-widget.ts | 28 +- .../search-in-workspace-widget.tsx | 99 +- .../theia/workspace/workspace-commands.ts | 70 +- .../workspace/workspace-delete-handler.ts | 21 +- .../workspace-frontend-contribution.ts | 24 +- .../theia/workspace/workspace-input-dialog.ts | 12 +- .../theia/workspace/workspace-service.ts | 50 +- .../workspace-variable-contribution.ts | 6 +- .../toolbar/arduino-toolbar-contribution.ts | 43 +- .../src/browser/toolbar/arduino-toolbar.tsx | 141 ++- .../src/browser/widgets/arduino-select.tsx | 53 +- .../component-list/component-list-item.tsx | 27 +- .../widgets/component-list/component-list.tsx | 40 +- .../filterable-list-container.tsx | 110 +- .../component-list/list-item-renderer.tsx | 119 ++- .../list-widget-frontend-contribution.ts | 10 +- .../widgets/component-list/list-widget.tsx | 87 +- .../widgets/component-list/search-bar.tsx | 20 +- .../src/common/protocol/arduino-component.ts | 21 +- .../src/common/protocol/boards-service.ts | 287 ++++-- .../src/common/protocol/config-service.ts | 52 +- .../src/common/protocol/core-service.ts | 17 +- .../src/common/protocol/examples-service.ts | 8 +- .../src/common/protocol/executable-service.ts | 2 +- .../src/common/protocol/installable.ts | 111 +- .../src/common/protocol/library-service.ts | 50 +- .../src/common/protocol/monitor-service.ts | 24 +- .../common/protocol/notification-service.ts | 11 +- .../protocol/sketches-service-client-impl.ts | 133 ++- .../src/common/protocol/sketches-service.ts | 70 +- arduino-ide-extension/src/common/utils.ts | 3 +- .../electron-window-service.ts | 19 +- .../theia/core/electron-main-menu-factory.ts | 82 +- .../theia/core/electron-menu-contribution.ts | 12 +- .../theia/core/electron-menu-module.ts | 18 +- .../arduino-electron-main-module.ts | 15 +- .../src/electron-main/splash/splash-screen.ts | 20 +- .../splash/splash-service-impl.ts | 4 +- .../theia/electron-main-application.ts | 85 +- .../theia/electron-main-window-service.ts | 7 +- .../src/node/arduino-daemon-impl.ts | 134 ++- .../src/node/arduino-ide-backend-module.ts | 263 +++-- .../src/node/board-discovery.ts | 54 +- .../src/node/boards-service-impl.ts | 338 ++++-- arduino-ide-extension/src/node/cli-config.ts | 94 +- .../src/node/config-service-impl.ts | 136 ++- .../src/node/core-client-provider.ts | 114 +- .../src/node/core-service-impl.ts | 116 ++- arduino-ide-extension/src/node/daemon-log.ts | 78 +- .../src/node/daemon-watcher.ts | 4 +- .../src/node/examples-service-impl.ts | 82 +- arduino-ide-extension/src/node/exec-util.ts | 50 +- .../src/node/executable-service-impl.ts | 12 +- .../src/node/grpc-client-provider.ts | 12 +- .../src/node/grpc-installable.ts | 63 +- .../src/node/library-service-server-impl.ts | 326 ++++-- .../node/monitor/monitor-client-provider.ts | 15 +- .../src/node/monitor/monitor-service-impl.ts | 137 ++- .../src/node/node-filesystem-ext.ts | 4 +- .../src/node/notification-service-server.ts | 48 +- .../src/node/sketches-service-impl.ts | 242 +++-- .../node/theia/core/backend-application.ts | 19 +- .../env-variables/env-variables-server.ts | 11 +- .../src/node/theia/git/git-init.ts | 30 +- .../workspace/default-workspace-server.ts | 6 +- .../src/test/common/boards-service.test.ts | 77 +- .../src/test/common/installable.test.ts | 54 +- .../src/test/node/arduino-daemon-impl.test.ts | 55 +- .../src/test/node/cli-config.test.ts | 61 +- .../src/test/node/exec-util.test.ts | 12 +- arduino-ide-extension/tslint.json | 38 - package.json | 21 +- yarn.lock | 975 +++++++++++++++++- 173 files changed, 8963 insertions(+), 3841 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .prettierrc delete mode 100644 arduino-ide-extension/tslint.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..af80f8d9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,49 @@ +module.exports = { + ignorePatterns: [ + 'node_modules/*', + '/*', + '!arduino-ide-extension', + 'arduino-ide-extension/*', + '!arduino-ide-extension/src', + 'arduino-ide-extension/src/node/cli-protocol', + ], + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + parserOptions: { + ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + ecmaFeatures: { + jsx: true, // Allows for the parsing of JSX + }, + }, + settings: { + react: { + version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use + }, + }, + extends: [ + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react + 'plugin:react-hooks/recommended', // Uses recommended rules from react hooks + 'plugin:prettier/recommended', + 'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier + ], + plugins: ['prettier'], + root: true, + rules: { + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-empty-function': 'warn', + '@typescript-eslint/no-empty-interface': 'warn', + 'react/display-name': 'warn', + eqeqeq: ['error', 'smart'], + 'guard-for-in': 'off', + 'id-blacklist': 'off', + 'id-match': 'off', + 'no-underscore-dangle': 'off', + 'no-unused-expressions': 'off', + 'no-var': 'error', + radix: 'error', + 'prettier/prettier': 'warn', + }, +}; diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..c99b8cca --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "singleQuote": true, + "tabWidth": 4, + "useTabs": false, + "overrides": [ + { + "files": "*.{json,yml}", + "options": { + "tabWidth": 2 + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b2540c1..72b85843 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,21 +1,9 @@ { - "tslint.enable": true, - "tslint.configFile": "./tslint.json", - "editor.formatOnSave": true, "files.exclude": { "**/lib": false }, - "editor.insertSpaces": true, - "editor.detectIndentation": false, - "[typescript]": { - "editor.tabSize": 4 + "typescript.tsdk": "node_modules/typescript/lib", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true }, - "[json]": { - "editor.tabSize": 2 - }, - "[jsonc]": { - "editor.tabSize": 2 - }, - "files.insertFinalNewline": true, - "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 439ef613..7fc579cf 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -10,7 +10,7 @@ "download-ls": "node ./scripts/download-ls.js", "download-examples": "node ./scripts/download-examples.js", "generate-protocol": "node ./scripts/generate-protocol.js", - "lint": "tslint -c ./tslint.json --project ./tsconfig.json", + "lint": "eslint", "build": "tsc && ncp ./src/node/cli-protocol/ ./lib/node/cli-protocol/ && yarn lint", "watch": "tsc -w", "test": "mocha \"./lib/test/**/*.test.js\"", diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts index 665c9fea..eb74e8b6 100644 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ b/arduino-ide-extension/src/browser/arduino-commands.ts @@ -4,20 +4,18 @@ import { Command } from '@theia/core/lib/common/command'; * @deprecated all these commands should go under contributions and have their command, menu, keybinding, and toolbar contributions. */ export namespace ArduinoCommands { - export const TOGGLE_COMPILE_FOR_DEBUG: Command = { - id: 'arduino-toggle-compile-for-debug' + id: 'arduino-toggle-compile-for-debug', }; /** * Unlike `OPEN_SKETCH`, it opens all files from a sketch folder. (ino, cpp, etc...) */ export const OPEN_SKETCH_FILES: Command = { - id: 'arduino-open-sketch-files' + id: 'arduino-open-sketch-files', }; export const OPEN_BOARDS_DIALOG: Command = { - id: 'arduino-open-boards-dialog' + id: 'arduino-open-boards-dialog', }; - } diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index db9d9a77..6d312946 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -1,18 +1,38 @@ import { Mutex } from 'async-mutex'; -import { MAIN_MENU_BAR, MenuContribution, MenuModelRegistry, SelectionService, ILogger, DisposableCollection } from '@theia/core'; +import { + MAIN_MENU_BAR, + MenuContribution, + MenuModelRegistry, + SelectionService, + ILogger, + DisposableCollection, +} from '@theia/core'; import { ContextMenuRenderer, - FrontendApplication, FrontendApplicationContribution, - OpenerService, StatusBar, StatusBarAlignment + FrontendApplication, + FrontendApplicationContribution, + OpenerService, + StatusBar, + StatusBarAlignment, } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; -import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { + TabBarToolbarContribution, + TabBarToolbarRegistry, +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { + CommandContribution, + CommandRegistry, +} from '@theia/core/lib/common/command'; import { MessageService } from '@theia/core/lib/common/message-service'; import URI from '@theia/core/lib/common/uri'; -import { EditorMainMenu, EditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser'; +import { + EditorMainMenu, + EditorManager, + EditorOpenerOptions, +} from '@theia/editor/lib/browser'; import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog'; import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; @@ -26,7 +46,14 @@ import { inject, injectable, postConstruct } from 'inversify'; import * as React from 'react'; import { remote } from 'electron'; import { MainMenuManager } from '../common/main-menu-manager'; -import { BoardsService, CoreService, Port, SketchesService, ExecutableService, Sketch } from '../common/protocol'; +import { + BoardsService, + CoreService, + Port, + SketchesService, + ExecutableService, + Sketch, +} from '../common/protocol'; import { ArduinoDaemon } from '../common/protocol/arduino-daemon'; import { ConfigService } from '../common/protocol/config-service'; import { FileSystemExt } from '../common/protocol/filesystem-ext'; @@ -52,9 +79,14 @@ import { FileChangeType } from '@theia/filesystem/lib/browser'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; @injectable() -export class ArduinoFrontendContribution implements FrontendApplicationContribution, - TabBarToolbarContribution, CommandContribution, MenuContribution, ColorContribution { - +export class ArduinoFrontendContribution + implements + FrontendApplicationContribution, + TabBarToolbarContribution, + CommandContribution, + MenuContribution, + ColorContribution +{ @inject(ILogger) protected logger: ILogger; @@ -163,48 +195,74 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut @inject(FrontendApplicationStateService) protected readonly appStateService: FrontendApplicationStateService; - protected invalidConfigPopup: Promise | undefined; + protected invalidConfigPopup: + | Promise + | undefined; protected toDisposeOnStop = new DisposableCollection(); @postConstruct() protected async init(): Promise { if (!window.navigator.onLine) { // tslint:disable-next-line:max-line-length - this.messageService.warn('You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.'); + this.messageService.warn( + 'You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.' + ); } - const updateStatusBar = ({ selectedBoard, selectedPort }: BoardsConfig.Config) => { + const updateStatusBar = ({ + selectedBoard, + selectedPort, + }: BoardsConfig.Config) => { this.statusBar.setElement('arduino-selected-board', { alignment: StatusBarAlignment.RIGHT, - text: selectedBoard ? `$(microchip) ${selectedBoard.name}` : '$(close) no board selected', - className: 'arduino-selected-board' + text: selectedBoard + ? `$(microchip) ${selectedBoard.name}` + : '$(close) no board selected', + className: 'arduino-selected-board', }); if (selectedBoard) { this.statusBar.setElement('arduino-selected-port', { alignment: StatusBarAlignment.RIGHT, - text: selectedPort ? `on ${Port.toString(selectedPort)}` : '[not connected]', - className: 'arduino-selected-port' + text: selectedPort + ? `on ${Port.toString(selectedPort)}` + : '[not connected]', + className: 'arduino-selected-port', }); } - } + }; this.boardsServiceClientImpl.onBoardsConfigChanged(updateStatusBar); updateStatusBar(this.boardsServiceClientImpl.boardsConfig); this.appStateService.reachedState('ready').then(async () => { const sketch = await this.sketchServiceClient.currentSketch(); - if (sketch && (!await this.sketchService.isTemp(sketch))) { - this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); - this.toDisposeOnStop.push(this.fileService.onDidFilesChange(async event => { - for (const { type, resource } of event.changes) { - if (type === FileChangeType.ADDED && resource.parent.toString() === sketch.uri) { - const reloadedSketch = await this.sketchService.loadSketch(sketch.uri) - if (Sketch.isInSketch(resource, reloadedSketch)) { - this.ensureOpened(resource.toString(), true, { mode: 'open' }); + if (sketch && !(await this.sketchService.isTemp(sketch))) { + this.toDisposeOnStop.push( + this.fileService.watch(new URI(sketch.uri)) + ); + this.toDisposeOnStop.push( + this.fileService.onDidFilesChange(async (event) => { + for (const { type, resource } of event.changes) { + if ( + type === FileChangeType.ADDED && + resource.parent.toString() === sketch.uri + ) { + const reloadedSketch = + await this.sketchService.loadSketch( + sketch.uri + ); + if ( + Sketch.isInSketch(resource, reloadedSketch) + ) { + this.ensureOpened( + resource.toString(), + true, + { mode: 'open' } + ); + } } } - } - })); + }) + ); } }); - } onStart(app: FrontendApplication): void { @@ -215,7 +273,8 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut this.outlineContribution, this.problemContribution, this.scmContribution, - this.siwContribution] as Array) { + this.siwContribution, + ] as Array) { if (viewContribution.initializeLayout) { viewContribution.initializeLayout(app); } @@ -229,18 +288,27 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut } }; this.boardsServiceClientImpl.onBoardsConfigChanged(start); - this.arduinoPreferences.onPreferenceChanged(event => { - if (event.preferenceName === 'arduino.language.log' && event.newValue !== event.oldValue) { + this.arduinoPreferences.onPreferenceChanged((event) => { + if ( + event.preferenceName === 'arduino.language.log' && + event.newValue !== event.oldValue + ) { start(this.boardsServiceClientImpl.boardsConfig); } }); this.arduinoPreferences.ready.then(() => { const webContents = remote.getCurrentWebContents(); - const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel'); + const zoomLevel = this.arduinoPreferences.get( + 'arduino.window.zoomLevel' + ); webContents.setZoomLevel(zoomLevel); }); - this.arduinoPreferences.onPreferenceChanged(event => { - if (event.preferenceName === 'arduino.window.zoomLevel' && typeof event.newValue === 'number' && event.newValue !== event.oldValue) { + this.arduinoPreferences.onPreferenceChanged((event) => { + if ( + event.preferenceName === 'arduino.window.zoomLevel' && + typeof event.newValue === 'number' && + event.newValue !== event.oldValue + ) { const webContents = remote.getCurrentWebContents(); webContents.setZoomLevel(event.newValue || 0); } @@ -254,21 +322,33 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut protected languageServerFqbn?: string; protected languageServerStartMutex = new Mutex(); - protected async startLanguageServer(fqbn: string, name: string | undefined): Promise { + protected async startLanguageServer( + fqbn: string, + name: string | undefined + ): Promise { const release = await this.languageServerStartMutex.acquire(); try { await this.hostedPluginSupport.didStart; const details = await this.boardsService.getBoardDetails({ fqbn }); if (!details) { // Core is not installed for the selected board. - console.info(`Could not start language server for ${fqbn}. The core is not installed for the board.`); + console.info( + `Could not start language server for ${fqbn}. The core is not installed for the board.` + ); if (this.languageServerFqbn) { try { - await this.commandRegistry.executeCommand('arduino.languageserver.stop'); - console.info(`Stopped language server process for ${this.languageServerFqbn}.`); + await this.commandRegistry.executeCommand( + 'arduino.languageserver.stop' + ); + console.info( + `Stopped language server process for ${this.languageServerFqbn}.` + ); this.languageServerFqbn = undefined; } catch (e) { - console.error(`Failed to start language server process for ${this.languageServerFqbn}`, e); + console.error( + `Failed to start language server process for ${this.languageServerFqbn}`, + e + ); throw e; } } @@ -282,31 +362,46 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut const log = this.arduinoPreferences.get('arduino.language.log'); let currentSketchPath: string | undefined = undefined; if (log) { - const currentSketch = await this.sketchServiceClient.currentSketch(); + const currentSketch = + await this.sketchServiceClient.currentSketch(); if (currentSketch) { - currentSketchPath = await this.fileService.fsPath(new URI(currentSketch.uri)); + currentSketchPath = await this.fileService.fsPath( + new URI(currentSketch.uri) + ); } } - const { clangdUri, cliUri, lsUri } = await this.executableService.list(); - const [clangdPath, cliPath, lsPath, cliConfigPath] = await Promise.all([ - this.fileService.fsPath(new URI(clangdUri)), - this.fileService.fsPath(new URI(cliUri)), - this.fileService.fsPath(new URI(lsUri)), - this.fileService.fsPath(new URI(await this.configService.getCliConfigFileUri())) - ]); + const { clangdUri, cliUri, lsUri } = + await this.executableService.list(); + const [clangdPath, cliPath, lsPath, cliConfigPath] = + await Promise.all([ + this.fileService.fsPath(new URI(clangdUri)), + this.fileService.fsPath(new URI(cliUri)), + this.fileService.fsPath(new URI(lsUri)), + this.fileService.fsPath( + new URI(await this.configService.getCliConfigFileUri()) + ), + ]); this.languageServerFqbn = await Promise.race([ - new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${20_000} ms.`)), 20_000)), - this.commandRegistry.executeCommand('arduino.languageserver.start', { - lsPath, - cliPath, - clangdPath, - log: currentSketchPath ? currentSketchPath : log, - cliConfigPath, - board: { - fqbn, - name: name ? `"${name}"` : undefined + new Promise((_, reject) => + setTimeout( + () => reject(new Error(`Timeout after ${20_000} ms.`)), + 20_000 + ) + ), + this.commandRegistry.executeCommand( + 'arduino.languageserver.start', + { + lsPath, + cliPath, + clangdPath, + log: currentSketchPath ? currentSketchPath : log, + cliConfigPath, + board: { + fqbn, + name: name ? `"${name}"` : undefined, + }, } - }) + ), ]); } catch (e) { console.log(`Failed to start language server for ${fqbn}`, e); @@ -319,29 +414,33 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: BoardsToolBarItem.TOOLBAR_ID, - render: () => , - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - priority: 7 + render: () => ( + + ), + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', + priority: 7, }); registry.registerItem({ id: 'toggle-serial-monitor', command: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR, - tooltip: 'Serial Monitor' + tooltip: 'Serial Monitor', }); } registerCommands(registry: CommandRegistry): void { registry.registerCommand(ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG, { execute: () => this.editorMode.toggleCompileForDebug(), - isToggled: () => this.editorMode.compileForDebug + isToggled: () => this.editorMode.compileForDebug, }); registry.registerCommand(ArduinoCommands.OPEN_SKETCH_FILES, { execute: async (uri: URI) => { this.openSketchFiles(uri); - } + }, }); registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, { execute: async (query?: string | undefined) => { @@ -349,7 +448,7 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut if (boardsConfig) { this.boardsServiceClientImpl.boardsConfig = boardsConfig; } - } + }, }); } @@ -358,10 +457,14 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut const index = menuPath.length - 1; const menuId = menuPath[index]; return menuId; - } - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(MonacoMenus.SELECTION)); + }; + registry + .getMenu(MAIN_MENU_BAR) + .removeNode(menuId(MonacoMenus.SELECTION)); registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(EditorMainMenu.GO)); - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(TerminalMenus.TERMINAL)); + registry + .getMenu(MAIN_MENU_BAR) + .removeNode(menuId(TerminalMenus.TERMINAL)); registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(CommonMenus.VIEW)); registry.registerSubmenu(ArduinoMenus.SKETCH, 'Sketch'); @@ -369,7 +472,7 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id, label: 'Optimize for Debugging', - order: '4' + order: '4', }); } @@ -383,11 +486,20 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut await this.ensureOpened(mainFileUri, true); if (mainFileUri.endsWith('.pde')) { const message = `The '${sketch.name}' still uses the old \`.pde\` format. Do you want to switch to the new \`.ino\` extension?`; - this.messageService.info(message, 'Later', 'Yes').then(async answer => { - if (answer === 'Yes') { - this.commandRegistry.executeCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH.id, { execOnlyIfTemp: false, openAfterMove: true, wipeOriginal: false }); - } - }); + this.messageService + .info(message, 'Later', 'Yes') + .then(async (answer) => { + if (answer === 'Yes') { + this.commandRegistry.executeCommand( + SaveAsSketch.Commands.SAVE_AS_SKETCH.id, + { + execOnlyIfTemp: false, + openAfterMove: true, + wipeOriginal: false, + } + ); + } + }); } } catch (e) { console.error(e); @@ -396,8 +508,14 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut } } - protected async ensureOpened(uri: string, forceOpen: boolean = false, options?: EditorOpenerOptions | undefined): Promise { - const widget = this.editorManager.all.find(widget => widget.editor.uri.toString() === uri); + protected async ensureOpened( + uri: string, + forceOpen = false, + options?: EditorOpenerOptions | undefined + ): Promise { + const widget = this.editorManager.all.find( + (widget) => widget.editor.uri.toString() === uri + ); if (!widget || forceOpen) { return this.editorManager.open(new URI(uri), options); } @@ -409,73 +527,78 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut id: 'arduino.branding.primary', defaults: { dark: 'statusBar.background', - light: 'statusBar.background' + light: 'statusBar.background', }, - description: 'The primary branding color, such as dialog titles, library, and board manager list labels.' + description: + 'The primary branding color, such as dialog titles, library, and board manager list labels.', }, { id: 'arduino.branding.secondary', defaults: { dark: 'statusBar.background', - light: 'statusBar.background' + light: 'statusBar.background', }, - description: 'Secondary branding color for list selections, dropdowns, and widget borders.' + description: + 'Secondary branding color for list selections, dropdowns, and widget borders.', }, { id: 'arduino.foreground', defaults: { dark: 'editorWidget.background', light: 'editorWidget.background', - hc: 'editorWidget.background' + hc: 'editorWidget.background', }, - description: 'Color of the Arduino IDE foreground which is used for dialogs, such as the Select Board dialog.' + description: + 'Color of the Arduino IDE foreground which is used for dialogs, such as the Select Board dialog.', }, { id: 'arduino.toolbar.background', defaults: { dark: 'button.background', light: 'button.background', - hc: 'activityBar.inactiveForeground' + hc: 'activityBar.inactiveForeground', }, - description: 'Background color of the toolbar items. Such as Upload, Verify, etc.' + description: + 'Background color of the toolbar items. Such as Upload, Verify, etc.', }, { id: 'arduino.toolbar.hoverBackground', defaults: { dark: 'button.hoverBackground', light: 'button.foreground', - hc: 'textLink.foreground' + hc: 'textLink.foreground', }, - description: 'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.' + description: + 'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.', }, { id: 'arduino.toolbar.toggleBackground', defaults: { dark: 'editor.selectionBackground', light: 'editor.selectionBackground', - hc: 'textPreformat.foreground' + hc: 'textPreformat.foreground', }, - description: 'Toggle color of the toolbar items when they are currently toggled (the command is in progress)' + description: + 'Toggle color of the toolbar items when they are currently toggled (the command is in progress)', }, { id: 'arduino.output.foreground', defaults: { dark: 'editor.foreground', light: 'editor.foreground', - hc: 'editor.foreground' + hc: 'editor.foreground', }, - description: 'Color of the text in the Output view.' + description: 'Color of the text in the Output view.', }, { id: 'arduino.output.background', defaults: { dark: 'editor.background', light: 'editor.background', - hc: 'editor.background' + hc: 'editor.background', }, - description: 'Background color of the Output view.' + description: 'Background color of the Output view.', } ); } - } diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index d4bfcbe7..ba580d8b 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -3,14 +3,29 @@ import { ContainerModule } from 'inversify'; import { WidgetFactory } from '@theia/core/lib/browser/widget-manager'; import { CommandContribution } from '@theia/core/lib/common/command'; import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; -import { TabBarToolbarContribution, TabBarToolbarFactory } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { + TabBarToolbarContribution, + TabBarToolbarFactory, +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider'; -import { FrontendApplicationContribution, FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application' +import { + FrontendApplicationContribution, + FrontendApplication as TheiaFrontendApplication, +} from '@theia/core/lib/browser/frontend-application'; import { LibraryListWidget } from './library/library-list-widget'; import { ArduinoFrontendContribution } from './arduino-frontend-contribution'; -import { LibraryService, LibraryServicePath } from '../common/protocol/library-service'; -import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service'; -import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service'; +import { + LibraryService, + LibraryServicePath, +} from '../common/protocol/library-service'; +import { + BoardsService, + BoardsServicePath, +} from '../common/protocol/boards-service'; +import { + SketchesService, + SketchesServicePath, +} from '../common/protocol/sketches-service'; import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl'; import { CoreService, CoreServicePath } from '../common/protocol/core-service'; import { BoardsListWidget } from './boards/boards-list-widget'; @@ -39,12 +54,15 @@ import { TabBarRendererFactory, ContextMenuRenderer, createTreeContainer, - TreeWidget + TreeWidget, } from '@theia/core/lib/browser'; import { MenuContribution } from '@theia/core/lib/common/menu'; import { ApplicationShell } from './theia/core/application-shell'; import { FrontendApplication } from './theia/core/frontend-application'; -import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog'; +import { + BoardsConfigDialog, + BoardsConfigDialogProps, +} from './boards/boards-config-dialog'; import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget'; import { ScmContribution as TheiaScmContribution } from '@theia/scm/lib/browser/scm-contribution'; import { ScmContribution } from './theia/scm/scm-contribution'; @@ -52,8 +70,15 @@ import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontend import { SearchInWorkspaceFrontendContribution } from './theia/search-in-workspace/search-in-workspace-frontend-contribution'; import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution'; import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl'; -import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service'; -import { ConfigService, ConfigServicePath } from '../common/protocol/config-service'; +import { + MonitorServicePath, + MonitorService, + MonitorServiceClient, +} from '../common/protocol/monitor-service'; +import { + ConfigService, + ConfigServicePath, +} from '../common/protocol/config-service'; import { MonitorWidget } from './monitor/monitor-widget'; import { MonitorViewContribution } from './monitor/monitor-view-contribution'; import { MonitorConnection } from './monitor/monitor-connection'; @@ -68,23 +93,35 @@ import { EditorMode } from './editor-mode'; import { ListItemRenderer } from './widgets/component-list/list-item-renderer'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service'; -import { ArduinoDaemonPath, ArduinoDaemon } from '../common/protocol/arduino-daemon'; +import { + ArduinoDaemonPath, + ArduinoDaemon, +} from '../common/protocol/arduino-daemon'; import { EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser'; -import { FrontendConnectionStatusService, ApplicationConnectionStatusContribution } from './theia/core/connection-status-service'; +import { + FrontendConnectionStatusService, + ApplicationConnectionStatusContribution, +} from './theia/core/connection-status-service'; import { FrontendConnectionStatusService as TheiaFrontendConnectionStatusService, - ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution + ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution, } from '@theia/core/lib/browser/connection-status-service'; import { BoardsDataMenuUpdater } from './boards/boards-data-menu-updater'; import { BoardsDataStore } from './boards/boards-data-store'; import { ILogger } from '@theia/core'; -import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext'; +import { + FileSystemExt, + FileSystemExtPath, +} from '../common/protocol/filesystem-ext'; import { WorkspaceFrontendContribution as TheiaWorkspaceFrontendContribution, FileMenuContribution as TheiaFileMenuContribution, - WorkspaceCommandContribution as TheiaWorkspaceCommandContribution + WorkspaceCommandContribution as TheiaWorkspaceCommandContribution, } from '@theia/workspace/lib/browser'; -import { WorkspaceFrontendContribution, ArduinoFileMenuContribution } from './theia/workspace/workspace-frontend-contribution'; +import { + WorkspaceFrontendContribution, + ArduinoFileMenuContribution, +} from './theia/workspace/workspace-frontend-contribution'; import { Contribution } from './contributions/contribution'; import { NewSketch } from './contributions/new-sketch'; import { OpenSketch } from './contributions/open-sketch'; @@ -111,19 +148,31 @@ import { EditorWidgetFactory } from './theia/editor/editor-widget-factory'; import { OutputWidget as TheiaOutputWidget } from '@theia/output/lib/browser/output-widget'; import { OutputWidget } from './theia/output/output-widget'; import { BurnBootloader } from './contributions/burn-bootloader'; -import { ExamplesServicePath, ExamplesService } from '../common/protocol/examples-service'; +import { + ExamplesServicePath, + ExamplesService, +} from '../common/protocol/examples-service'; import { BuiltInExamples, LibraryExamples } from './contributions/examples'; import { IncludeLibrary } from './contributions/include-library'; import { OutputChannelManager as TheiaOutputChannelManager } from '@theia/output/lib/common/output-channel'; import { OutputChannelManager } from './theia/output/output-channel'; -import { OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl, OutputChannelRegistryMainImpl } from './theia/plugin-ext/output-channel-registry-main'; +import { + OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl, + OutputChannelRegistryMainImpl, +} from './theia/plugin-ext/output-channel-registry-main'; import { ExecutableService, ExecutableServicePath } from '../common/protocol'; import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service'; import { ResponseServiceImpl } from './response-service-impl'; -import { ResponseServicePath, ResponseService } from '../common/protocol/response-service'; +import { + ResponseServicePath, + ResponseService, +} from '../common/protocol/response-service'; import { NotificationCenter } from './notification-center'; -import { NotificationServicePath, NotificationServiceServer } from '../common/protocol'; +import { + NotificationServicePath, + NotificationServiceServer, +} from '../common/protocol'; import { About } from './contributions/about'; import { IconThemeService } from '@theia/core/lib/browser/icon-theme-service'; import { TabBarRenderer } from './theia/core/tab-bars'; @@ -139,8 +188,13 @@ import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationCo import { BoardSelection } from './contributions/board-selection'; import { OpenRecentSketch } from './contributions/open-recent-sketch'; import { Help } from './contributions/help'; -import { bindArduinoPreferences } from './arduino-preferences' -import { SettingsService, SettingsDialog, SettingsWidget, SettingsDialogProps } from './settings'; +import { bindArduinoPreferences } from './arduino-preferences'; +import { + SettingsService, + SettingsDialog, + SettingsWidget, + SettingsDialogProps, +} from './settings'; import { AddFile } from './contributions/add-file'; import { ArchiveSketch } from './contributions/archive-sketch'; import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution'; @@ -170,7 +224,7 @@ MonacoThemingService.register({ id: 'arduino-theme', label: 'Light (Arduino)', uiTheme: 'vs', - json: require('../../src/browser/data/arduino.color-theme.json') + json: require('../../src/browser/data/arduino.color-theme.json'), }); export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -182,7 +236,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CommandContribution).toService(ArduinoFrontendContribution); bind(MenuContribution).toService(ArduinoFrontendContribution); bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution); - bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution); + bind(FrontendApplicationContribution).toService( + ArduinoFrontendContribution + ); bind(ColorContribution).toService(ArduinoFrontendContribution); bind(ArduinoToolbarContribution).toSelf().inSingletonScope(); @@ -192,40 +248,75 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ListItemRenderer).toSelf().inSingletonScope(); // Library service - bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope(); + bind(LibraryService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + LibraryServicePath + ) + ) + .inSingletonScope(); // Library list widget bind(LibraryListWidget).toSelf(); bindViewContribution(bind, LibraryListWidgetFrontendContribution); - bind(WidgetFactory).toDynamicValue(context => ({ + bind(WidgetFactory).toDynamicValue((context) => ({ id: LibraryListWidget.WIDGET_ID, - createWidget: () => context.container.get(LibraryListWidget) + createWidget: () => context.container.get(LibraryListWidget), })); - bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution); + bind(FrontendApplicationContribution).toService( + LibraryListWidgetFrontendContribution + ); // Sketch list service - bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope(); + bind(SketchesService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + SketchesServicePath + ) + ) + .inSingletonScope(); bind(SketchesServiceClientImpl).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(SketchesServiceClientImpl); // Config service - bind(ConfigService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ConfigServicePath)).inSingletonScope(); + bind(ConfigService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + ConfigServicePath + ) + ) + .inSingletonScope(); // Boards service - bind(BoardsService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath)).inSingletonScope(); + bind(BoardsService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + BoardsServicePath + ) + ) + .inSingletonScope(); // Boards service client to receive and delegate notifications from the backend. bind(BoardsServiceProvider).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(BoardsServiceProvider); // To be able to track, and update the menu based on the core settings (aka. board details) of the currently selected board. - bind(FrontendApplicationContribution).to(BoardsDataMenuUpdater).inSingletonScope(); + bind(FrontendApplicationContribution) + .to(BoardsDataMenuUpdater) + .inSingletonScope(); bind(BoardsDataStore).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(BoardsDataStore); // Logger for the Arduino daemon - bind(ILogger).toDynamicValue(ctx => { - const parentLogger = ctx.container.get(ILogger); - return parentLogger.child('store'); - }).inSingletonScope().whenTargetNamed('store'); + bind(ILogger) + .toDynamicValue((ctx) => { + const parentLogger = ctx.container.get(ILogger); + return parentLogger.child('store'); + }) + .inSingletonScope() + .whenTargetNamed('store'); // Boards auto-installer bind(BoardsAutoInstaller).toSelf().inSingletonScope(); @@ -234,21 +325,30 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // Boards list widget bind(BoardsListWidget).toSelf(); bindViewContribution(bind, BoardsListWidgetFrontendContribution); - bind(WidgetFactory).toDynamicValue(context => ({ + bind(WidgetFactory).toDynamicValue((context) => ({ id: BoardsListWidget.WIDGET_ID, - createWidget: () => context.container.get(BoardsListWidget) + createWidget: () => context.container.get(BoardsListWidget), })); - bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution); + bind(FrontendApplicationContribution).toService( + BoardsListWidgetFrontendContribution + ); // Board select dialog bind(BoardsConfigDialogWidget).toSelf().inSingletonScope(); bind(BoardsConfigDialog).toSelf().inSingletonScope(); bind(BoardsConfigDialogProps).toConstantValue({ - title: 'Select Board' - }) + title: 'Select Board', + }); // Core service - bind(CoreService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, CoreServicePath)).inSingletonScope(); + bind(CoreService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + CoreServicePath + ) + ) + .inSingletonScope(); // Serial monitor bind(MonitorModel).toSelf().inSingletonScope(); @@ -256,64 +356,103 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonitorWidget).toSelf(); bindViewContribution(bind, MonitorViewContribution); bind(TabBarToolbarContribution).toService(MonitorViewContribution); - bind(WidgetFactory).toDynamicValue(context => ({ + bind(WidgetFactory).toDynamicValue((context) => ({ id: MonitorWidget.ID, - createWidget: () => context.container.get(MonitorWidget) + createWidget: () => context.container.get(MonitorWidget), })); // Frontend binding for the serial monitor service - bind(MonitorService).toDynamicValue(context => { - const connection = context.container.get(WebSocketConnectionProvider); - const client = context.container.get(MonitorServiceClientImpl); - return connection.createProxy(MonitorServicePath, client); - }).inSingletonScope(); + bind(MonitorService) + .toDynamicValue((context) => { + const connection = context.container.get( + WebSocketConnectionProvider + ); + const client = context.container.get(MonitorServiceClientImpl); + return connection.createProxy(MonitorServicePath, client); + }) + .inSingletonScope(); bind(MonitorConnection).toSelf().inSingletonScope(); // Serial monitor service client to receive and delegate notifications from the backend. bind(MonitorServiceClientImpl).toSelf().inSingletonScope(); - bind(MonitorServiceClient).toDynamicValue(context => { - const client = context.container.get(MonitorServiceClientImpl); - WebSocketConnectionProvider.createProxy(context.container, MonitorServicePath, client); - return client; - }).inSingletonScope(); + bind(MonitorServiceClient) + .toDynamicValue((context) => { + const client = context.container.get(MonitorServiceClientImpl); + WebSocketConnectionProvider.createProxy( + context.container, + MonitorServicePath, + client + ); + return client; + }) + .inSingletonScope(); bind(WorkspaceService).toSelf().inSingletonScope(); rebind(TheiaWorkspaceService).toService(WorkspaceService); bind(WorkspaceVariableContribution).toSelf().inSingletonScope(); - rebind(TheiaWorkspaceVariableContribution).toService(WorkspaceVariableContribution); + rebind(TheiaWorkspaceVariableContribution).toService( + WorkspaceVariableContribution + ); // Customizing default Theia layout based on the editor mode: `pro-mode` or `classic`. bind(EditorMode).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(EditorMode); // Layout and shell customizations. - rebind(TheiaOutlineViewContribution).to(OutlineViewContribution).inSingletonScope(); + rebind(TheiaOutlineViewContribution) + .to(OutlineViewContribution) + .inSingletonScope(); rebind(TheiaProblemContribution).to(ProblemContribution).inSingletonScope(); - rebind(TheiaFileNavigatorContribution).to(FileNavigatorContribution).inSingletonScope(); - rebind(TheiaKeymapsFrontendContribution).to(KeymapsFrontendContribution).inSingletonScope(); + rebind(TheiaFileNavigatorContribution) + .to(FileNavigatorContribution) + .inSingletonScope(); + rebind(TheiaKeymapsFrontendContribution) + .to(KeymapsFrontendContribution) + .inSingletonScope(); rebind(TheiaEditorContribution).to(EditorContribution).inSingletonScope(); - rebind(TheiaMonacoStatusBarContribution).to(MonacoStatusBarContribution).inSingletonScope(); + rebind(TheiaMonacoStatusBarContribution) + .to(MonacoStatusBarContribution) + .inSingletonScope(); rebind(TheiaApplicationShell).to(ApplicationShell).inSingletonScope(); rebind(TheiaScmContribution).to(ScmContribution).inSingletonScope(); - rebind(TheiaSearchInWorkspaceFrontendContribution).to(SearchInWorkspaceFrontendContribution).inSingletonScope(); + rebind(TheiaSearchInWorkspaceFrontendContribution) + .to(SearchInWorkspaceFrontendContribution) + .inSingletonScope(); rebind(TheiaFrontendApplication).to(FrontendApplication).inSingletonScope(); - rebind(TheiaWorkspaceFrontendContribution).to(WorkspaceFrontendContribution).inSingletonScope(); - rebind(TheiaFileMenuContribution).to(ArduinoFileMenuContribution).inSingletonScope(); - rebind(TheiaCommonFrontendContribution).to(CommonFrontendContribution).inSingletonScope(); - rebind(TheiaPreferencesContribution).to(PreferencesContribution).inSingletonScope(); + rebind(TheiaWorkspaceFrontendContribution) + .to(WorkspaceFrontendContribution) + .inSingletonScope(); + rebind(TheiaFileMenuContribution) + .to(ArduinoFileMenuContribution) + .inSingletonScope(); + rebind(TheiaCommonFrontendContribution) + .to(CommonFrontendContribution) + .inSingletonScope(); + rebind(TheiaPreferencesContribution) + .to(PreferencesContribution) + .inSingletonScope(); rebind(TheiaKeybindingRegistry).to(KeybindingRegistry).inSingletonScope(); - rebind(TheiaWorkspaceCommandContribution).to(WorkspaceCommandContribution).inSingletonScope(); - rebind(TheiaWorkspaceDeleteHandler).to(WorkspaceDeleteHandler).inSingletonScope(); + rebind(TheiaWorkspaceCommandContribution) + .to(WorkspaceCommandContribution) + .inSingletonScope(); + rebind(TheiaWorkspaceDeleteHandler) + .to(WorkspaceDeleteHandler) + .inSingletonScope(); rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope(); - rebind(TabBarToolbarFactory).toFactory(({ container: parentContainer }) => () => { - const container = parentContainer.createChild(); - container.bind(TabBarToolbar).toSelf().inSingletonScope(); - return container.get(TabBarToolbar); - }); + rebind(TabBarToolbarFactory).toFactory( + ({ container: parentContainer }) => + () => { + const container = parentContainer.createChild(); + container.bind(TabBarToolbar).toSelf().inSingletonScope(); + return container.get(TabBarToolbar); + } + ); bind(OutputWidget).toSelf().inSingletonScope(); rebind(TheiaOutputWidget).toService(OutputWidget); bind(OutputChannelManager).toSelf().inSingletonScope(); rebind(TheiaOutputChannelManager).toService(OutputChannelManager); bind(OutputChannelRegistryMainImpl).toSelf().inTransientScope(); - rebind(TheiaOutputChannelRegistryMainImpl).toService(OutputChannelRegistryMainImpl); + rebind(TheiaOutputChannelRegistryMainImpl).toService( + OutputChannelRegistryMainImpl + ); bind(MonacoTextModelService).toSelf().inSingletonScope(); rebind(TheiaMonacoTextModelService).toService(MonacoTextModelService); bind(MonacoEditorProvider).toSelf().inSingletonScope(); @@ -321,18 +460,26 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(SearchInWorkspaceWidget).toSelf(); rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget); - rebind(TheiaSearchInWorkspaceResultTreeWidget).toDynamicValue(({ container }) => { - const childContainer = createTreeContainer(container); - childContainer.bind(SearchInWorkspaceResultTreeWidget).toSelf() - childContainer.rebind(TreeWidget).toService(SearchInWorkspaceResultTreeWidget); - return childContainer.get(SearchInWorkspaceResultTreeWidget); - }); + rebind(TheiaSearchInWorkspaceResultTreeWidget).toDynamicValue( + ({ container }) => { + const childContainer = createTreeContainer(container); + childContainer.bind(SearchInWorkspaceResultTreeWidget).toSelf(); + childContainer + .rebind(TreeWidget) + .toService(SearchInWorkspaceResultTreeWidget); + return childContainer.get(SearchInWorkspaceResultTreeWidget); + } + ); // Show a disconnected status bar, when the daemon is not available bind(ApplicationConnectionStatusContribution).toSelf().inSingletonScope(); - rebind(TheiaApplicationConnectionStatusContribution).toService(ApplicationConnectionStatusContribution); + rebind(TheiaApplicationConnectionStatusContribution).toService( + ApplicationConnectionStatusContribution + ); bind(FrontendConnectionStatusService).toSelf().inSingletonScope(); - rebind(TheiaFrontendConnectionStatusService).toService(FrontendConnectionStatusService); + rebind(TheiaFrontendConnectionStatusService).toService( + FrontendConnectionStatusService + ); // Decorator customizations bind(TabBarDecoratorService).toSelf().inSingletonScope(); @@ -350,16 +497,44 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(OutputToolbarContribution).toSelf().inSingletonScope(); rebind(TheiaOutputToolbarContribution).toService(OutputToolbarContribution); - bind(ArduinoDaemon).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ArduinoDaemonPath)).inSingletonScope(); + bind(ArduinoDaemon) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + ArduinoDaemonPath + ) + ) + .inSingletonScope(); // File-system extension - bind(FileSystemExt).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, FileSystemExtPath)).inSingletonScope(); + bind(FileSystemExt) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + FileSystemExtPath + ) + ) + .inSingletonScope(); // Examples service@ - bind(ExamplesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ExamplesServicePath)).inSingletonScope(); + bind(ExamplesService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + ExamplesServicePath + ) + ) + .inSingletonScope(); // Executable URIs known by the backend - bind(ExecutableService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ExecutableServicePath)).inSingletonScope(); + bind(ExecutableService) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + ExecutableServicePath + ) + ) + .inSingletonScope(); Contribution.configure(bind, NewSketch); Contribution.configure(bind, OpenSketch); @@ -387,22 +562,44 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, ArchiveSketch); Contribution.configure(bind, AddZipLibrary); - bind(ResponseServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, responseService) => { - WebSocketConnectionProvider.createProxy(container, ResponseServicePath, responseService); - return responseService; - }); + bind(ResponseServiceImpl) + .toSelf() + .inSingletonScope() + .onActivation(({ container }, responseService) => { + WebSocketConnectionProvider.createProxy( + container, + ResponseServicePath, + responseService + ); + return responseService; + }); bind(ResponseService).toService(ResponseServiceImpl); bind(NotificationCenter).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(NotificationCenter); - bind(NotificationServiceServer).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, NotificationServicePath)).inSingletonScope(); + bind(NotificationServiceServer) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + NotificationServicePath + ) + ) + .inSingletonScope(); // Enable the dirty indicator on uncloseable widgets. - rebind(TabBarRendererFactory).toFactory(context => () => { - const contextMenuRenderer = context.container.get(ContextMenuRenderer); - const decoratorService = context.container.get(TabBarDecoratorService); - const iconThemeService = context.container.get(IconThemeService); - return new TabBarRenderer(contextMenuRenderer, decoratorService, iconThemeService); + rebind(TabBarRendererFactory).toFactory((context) => () => { + const contextMenuRenderer = + context.container.get(ContextMenuRenderer); + const decoratorService = context.container.get( + TabBarDecoratorService + ); + const iconThemeService = + context.container.get(IconThemeService); + return new TabBarRenderer( + contextMenuRenderer, + decoratorService, + iconThemeService + ); }); // Workaround for https://github.com/eclipse-theia/theia/issues/8722 @@ -419,15 +616,23 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { rebind(TheiaDebugSessionManager).toService(DebugSessionManager); // To remove the `Run` menu item from the application menu. bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope(); - rebind(TheiaDebugFrontendApplicationContribution).toService(DebugFrontendApplicationContribution); + rebind(TheiaDebugFrontendApplicationContribution).toService( + DebugFrontendApplicationContribution + ); // To be able to use a `launch.json` from outside of the workspace. bind(DebugConfigurationManager).toSelf().inSingletonScope(); rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager); // Patch for the debug hover: https://github.com/eclipse-theia/theia/pull/9256/ - rebind(DebugEditorModelFactory).toDynamicValue(({ container }) => (editor => - DebugEditorModel.createModel(container, editor) - )).inSingletonScope(); + rebind(DebugEditorModelFactory) + .toDynamicValue( + ({ container }) => + ( + ((editor) => + DebugEditorModel.createModel(container, editor)) + ) + ) + .inSingletonScope(); // Preferences bindArduinoPreferences(bind); @@ -438,7 +643,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(SettingsWidget).toSelf().inSingletonScope(); bind(SettingsDialog).toSelf().inSingletonScope(); bind(SettingsDialogProps).toConstantValue({ - title: 'Preferences' + title: 'Preferences', }); bind(StorageWrapper).toSelf().inSingletonScope(); diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index 919cd8e5..b0dd1177 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -1,50 +1,61 @@ import { interfaces } from 'inversify'; -import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser/preferences'; +import { + createPreferenceProxy, + PreferenceProxy, + PreferenceService, + PreferenceContribution, + PreferenceSchema, +} from '@theia/core/lib/browser/preferences'; import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol'; export const ArduinoConfigSchema: PreferenceSchema = { - 'type': 'object', - 'properties': { + type: 'object', + properties: { 'arduino.language.log': { - 'type': 'boolean', - 'description': "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", - 'default': false + type: 'boolean', + description: + "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + default: false, }, 'arduino.compile.verbose': { - 'type': 'boolean', - 'description': 'True for verbose compile output. False by default', - 'default': false + type: 'boolean', + description: 'True for verbose compile output. False by default', + default: false, }, 'arduino.compile.warnings': { - 'enum': [...CompilerWarningLiterals], - 'description': "Tells gcc which warning level to use. It's 'None' by default", - 'default': 'None' + enum: [...CompilerWarningLiterals], + description: + "Tells gcc which warning level to use. It's 'None' by default", + default: 'None', }, 'arduino.upload.verbose': { - 'type': 'boolean', - 'description': 'True for verbose upload output. False by default.', - 'default': false + type: 'boolean', + description: 'True for verbose upload output. False by default.', + default: false, }, 'arduino.upload.verify': { - 'type': 'boolean', - 'default': false + type: 'boolean', + default: false, }, 'arduino.window.autoScale': { - 'type': 'boolean', - 'description': 'True if the user interface automatically scales with the font size.', - 'default': true + type: 'boolean', + description: + 'True if the user interface automatically scales with the font size.', + default: true, }, 'arduino.window.zoomLevel': { - 'type': 'number', - 'description': 'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.', - 'default': 0 + type: 'number', + description: + 'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.', + default: 0, }, 'arduino.ide.autoUpdate': { - 'type': 'boolean', - 'description': 'True to enable automatic update checks. The IDE will check for updates automatically and periodically.', - 'default': true - } - } + type: 'boolean', + description: + 'True to enable automatic update checks. The IDE will check for updates automatically and periodically.', + default: true, + }, + }, }; export interface ArduinoConfiguration { @@ -61,14 +72,19 @@ export interface ArduinoConfiguration { export const ArduinoPreferences = Symbol('ArduinoPreferences'); export type ArduinoPreferences = PreferenceProxy; -export function createArduinoPreferences(preferences: PreferenceService): ArduinoPreferences { +export function createArduinoPreferences( + preferences: PreferenceService +): ArduinoPreferences { return createPreferenceProxy(preferences, ArduinoConfigSchema); } export function bindArduinoPreferences(bind: interfaces.Bind): void { - bind(ArduinoPreferences).toDynamicValue(ctx => { - const preferences = ctx.container.get(PreferenceService); + bind(ArduinoPreferences).toDynamicValue((ctx) => { + const preferences = + ctx.container.get(PreferenceService); return createArduinoPreferences(preferences); }); - bind(PreferenceContribution).toConstantValue({ schema: ArduinoConfigSchema }); + bind(PreferenceContribution).toConstantValue({ + schema: ArduinoConfigSchema, + }); } diff --git a/arduino-ide-extension/src/browser/arduino-workspace-resolver.ts b/arduino-ide-extension/src/browser/arduino-workspace-resolver.ts index b5b96625..0db1aeaf 100644 --- a/arduino-ide-extension/src/browser/arduino-workspace-resolver.ts +++ b/arduino-ide-extension/src/browser/arduino-workspace-resolver.ts @@ -7,7 +7,7 @@ import { MaybePromise } from '@theia/core/lib/common/types'; /** * Class for determining the default workspace location from the * `location.hash`, the historical workspace locations, and recent sketch files. - * + * * The following logic is used for determining the default workspace location: * - `hash` points to an existing location? * - Yes @@ -24,20 +24,24 @@ namespace ArduinoWorkspaceRootResolver { readonly isValid: (uri: string) => MaybePromise; } export interface ResolveOptions { - readonly hash?: string + readonly hash?: string; readonly recentWorkspaces: string[]; // Gathered from the default sketch folder. The default sketch folder is defined by the CLI. readonly recentSketches: string[]; } } export class ArduinoWorkspaceRootResolver { + constructor(protected options: ArduinoWorkspaceRootResolver.InitOptions) {} - constructor(protected options: ArduinoWorkspaceRootResolver.InitOptions) { - } - - async resolve(options: ArduinoWorkspaceRootResolver.ResolveOptions): Promise<{ uri: string } | undefined> { + async resolve( + options: ArduinoWorkspaceRootResolver.ResolveOptions + ): Promise<{ uri: string } | undefined> { const { hash, recentWorkspaces, recentSketches } = options; - for (const uri of [this.hashToUri(hash), ...recentWorkspaces, ...recentSketches].filter(notEmpty)) { + for (const uri of [ + this.hashToUri(hash), + ...recentWorkspaces, + ...recentSketches, + ].filter(notEmpty)) { const valid = await this.isValid(uri); if (valid) { return { uri }; @@ -56,13 +60,14 @@ export class ArduinoWorkspaceRootResolver { // - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L143 and // - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L423 protected hashToUri(hash: string | undefined): string | undefined { - if (hash - && hash.length > 1 - && hash.startsWith('#')) { + if (hash && hash.length > 1 && hash.startsWith('#')) { const path = hash.slice(1); // Trim the leading `#`. - return new URI(toUnix(path.slice(isWindows && hash.startsWith('/') ? 1 : 0))).withScheme('file').toString(); + return new URI( + toUnix(path.slice(isWindows && hash.startsWith('/') ? 1 : 0)) + ) + .withScheme('file') + .toString(); } return undefined; } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts b/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts index e7293b17..68d1c65b 100644 --- a/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts +++ b/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts @@ -1,7 +1,11 @@ import { injectable, inject } from 'inversify'; import { MessageService } from '@theia/core/lib/common/message-service'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; -import { BoardsService, BoardsPackage, Board } from '../../common/protocol/boards-service'; +import { + BoardsService, + BoardsPackage, + Board, +} from '../../common/protocol/boards-service'; import { BoardsServiceProvider } from './boards-service-provider'; import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution'; import { BoardsConfig } from './boards-config'; @@ -14,7 +18,6 @@ import { ResponseServiceImpl } from '../response-service-impl'; */ @injectable() export class BoardsAutoInstaller implements FrontendApplicationContribution { - @inject(MessageService) protected readonly messageService: MessageService; @@ -34,57 +37,92 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution { protected notifications: Board[] = []; onStart(): void { - this.boardsServiceClient.onBoardsConfigChanged(this.ensureCoreExists.bind(this)); + this.boardsServiceClient.onBoardsConfigChanged( + this.ensureCoreExists.bind(this) + ); this.ensureCoreExists(this.boardsServiceClient.boardsConfig); } protected ensureCoreExists(config: BoardsConfig.Config): void { const { selectedBoard } = config; - if (selectedBoard && !this.notifications.find(board => Board.sameAs(board, selectedBoard))) { + if ( + selectedBoard && + !this.notifications.find((board) => + Board.sameAs(board, selectedBoard) + ) + ) { this.notifications.push(selectedBoard); - this.boardsService.search({}).then(packages => { - + this.boardsService.search({}).then((packages) => { // filter packagesForBoard selecting matches from the cli (installed packages) // and matches based on the board name // NOTE: this ensures the Deprecated & new packages are all in the array // so that we can check if any of the valid packages is already installed - const packagesForBoard = packages.filter(pkg => BoardsPackage.contains(selectedBoard, pkg) || pkg.boards.some(board => board.name === selectedBoard.name)); + const packagesForBoard = packages.filter( + (pkg) => + BoardsPackage.contains(selectedBoard, pkg) || + pkg.boards.some( + (board) => board.name === selectedBoard.name + ) + ); // check if one of the packages for the board is already installed. if so, no hint - if (packagesForBoard.some(({ installedVersion }) => !!installedVersion)) { return; } + if ( + packagesForBoard.some( + ({ installedVersion }) => !!installedVersion + ) + ) { + return; + } // filter the installable (not installed) packages, // CLI returns the packages already sorted with the deprecated ones at the end of the list // in order to ensure the new ones are preferred - const candidates = packagesForBoard - .filter(({ installable, installedVersion }) => installable && !installedVersion); + const candidates = packagesForBoard.filter( + ({ installable, installedVersion }) => + installable && !installedVersion + ); const candidate = candidates[0]; if (candidate) { - const version = candidate.availableVersions[0] ? `[v ${candidate.availableVersions[0]}]` : ''; + const version = candidate.availableVersions[0] + ? `[v ${candidate.availableVersions[0]}]` + : ''; // tslint:disable-next-line:max-line-length - this.messageService.info(`The \`"${candidate.name} ${version}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Install Manually', 'Yes').then(async answer => { - const index = this.notifications.findIndex(board => Board.sameAs(board, selectedBoard)); - if (index !== -1) { - this.notifications.splice(index, 1); - } - if (answer === 'Yes') { - await Installable.installWithProgress({ - installable: this.boardsService, - item: candidate, - messageService: this.messageService, - responseService: this.responseService, - version: candidate.availableVersions[0] - }); - return - } - if (answer) { - this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase())); - } - }); + this.messageService + .info( + `The \`"${candidate.name} ${version}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, + 'Install Manually', + 'Yes' + ) + .then(async (answer) => { + const index = this.notifications.findIndex( + (board) => Board.sameAs(board, selectedBoard) + ); + if (index !== -1) { + this.notifications.splice(index, 1); + } + if (answer === 'Yes') { + await Installable.installWithProgress({ + installable: this.boardsService, + item: candidate, + messageService: this.messageService, + responseService: this.responseService, + version: candidate.availableVersions[0], + }); + return; + } + if (answer) { + this.boardsManagerFrontendContribution + .openView({ reveal: true }) + .then((widget) => + widget.refresh( + candidate.name.toLocaleLowerCase() + ) + ); + } + }); } - }) + }); } } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-config-dialog-widget.tsx b/arduino-ide-extension/src/browser/boards/boards-config-dialog-widget.tsx index 921d6efe..ecb01b3c 100644 --- a/arduino-ide-extension/src/browser/boards/boards-config-dialog-widget.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-config-dialog-widget.tsx @@ -9,7 +9,6 @@ import { NotificationCenter } from '../notification-center'; @injectable() export class BoardsConfigDialogWidget extends ReactWidget { - @inject(BoardsService) protected readonly boardsService: BoardsService; @@ -20,7 +19,8 @@ export class BoardsConfigDialogWidget extends ReactWidget { protected readonly notificationCenter: NotificationCenter; protected readonly onFilterTextDidChangeEmitter = new Emitter(); - protected readonly onBoardConfigChangedEmitter = new Emitter(); + protected readonly onBoardConfigChangedEmitter = + new Emitter(); readonly onBoardConfigChanged = this.onBoardConfigChangedEmitter.event; protected focusNode: HTMLElement | undefined; @@ -30,7 +30,7 @@ export class BoardsConfigDialogWidget extends ReactWidget { this.id = 'select-board-dialog'; this.toDispose.pushAll([ this.onBoardConfigChangedEmitter, - this.onFilterTextDidChangeEmitter + this.onFilterTextDidChangeEmitter, ]); } @@ -40,21 +40,26 @@ export class BoardsConfigDialogWidget extends ReactWidget { protected fireConfigChanged = (config: BoardsConfig.Config) => { this.onBoardConfigChangedEmitter.fire(config); - } + }; protected setFocusNode = (element: HTMLElement | undefined) => { this.focusNode = element; - } + }; protected render(): React.ReactNode { - return
- -
; + return ( +
+ +
+ ); } protected onActivateRequest(msg: Message): void { @@ -64,5 +69,4 @@ export class BoardsConfigDialogWidget extends ReactWidget { } (this.focusNode || this.node).focus(); } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts b/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts index 2786c69a..830db7d8 100644 --- a/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts +++ b/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts @@ -1,18 +1,21 @@ import { injectable, inject, postConstruct } from 'inversify'; import { Message } from '@phosphor/messaging'; -import { AbstractDialog, DialogProps, Widget, DialogError } from '@theia/core/lib/browser'; +import { + AbstractDialog, + DialogProps, + Widget, + DialogError, +} from '@theia/core/lib/browser'; import { BoardsConfig } from './boards-config'; import { BoardsService } from '../../common/protocol/boards-service'; import { BoardsServiceProvider } from './boards-service-provider'; import { BoardsConfigDialogWidget } from './boards-config-dialog-widget'; @injectable() -export class BoardsConfigDialogProps extends DialogProps { -} +export class BoardsConfigDialogProps extends DialogProps {} @injectable() export class BoardsConfigDialog extends AbstractDialog { - @inject(BoardsConfigDialogWidget) protected readonly widget: BoardsConfigDialogWidget; @@ -24,7 +27,10 @@ export class BoardsConfigDialog extends AbstractDialog { protected config: BoardsConfig.Config = {}; - constructor(@inject(BoardsConfigDialogProps) protected readonly props: BoardsConfigDialogProps) { + constructor( + @inject(BoardsConfigDialogProps) + protected readonly props: BoardsConfigDialogProps + ) { super(props); this.contentNode.classList.add('select-board-dialog'); @@ -36,16 +42,20 @@ export class BoardsConfigDialog extends AbstractDialog { @postConstruct() protected init(): void { - this.toDispose.push(this.boardsServiceClient.onBoardsConfigChanged(config => { - this.config = config; - this.update(); - })); + this.toDispose.push( + this.boardsServiceClient.onBoardsConfigChanged((config) => { + this.config = config; + this.update(); + }) + ); } /** * Pass in an empty string if you want to reset the search term. Using `undefined` has no effect. */ - async open(query: string | undefined = undefined): Promise { + async open( + query: string | undefined = undefined + ): Promise { if (typeof query === 'string') { this.widget.search(query); } @@ -67,7 +77,7 @@ export class BoardsConfigDialog extends AbstractDialog { for (const paragraph of [ 'Select both a Board and a Port if you want to upload a sketch.', - 'If you only select a Board you will be able just to compile, but not to upload your sketch.' + 'If you only select a Board you will be able just to compile, but not to upload your sketch.', ]) { const p = document.createElement('div'); p.textContent = paragraph; @@ -82,10 +92,12 @@ export class BoardsConfigDialog extends AbstractDialog { Widget.detach(this.widget); } Widget.attach(this.widget, this.contentNode); - this.toDisposeOnDetach.push(this.widget.onBoardConfigChanged(config => { - this.config = config; - this.update(); - })); + this.toDisposeOnDetach.push( + this.widget.onBoardConfigChanged((config) => { + this.config = config; + this.update(); + }) + ); super.onAfterAttach(msg); this.update(); } @@ -119,5 +131,4 @@ export class BoardsConfigDialog extends AbstractDialog { get value(): BoardsConfig.Config { return this.config; } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-config.tsx b/arduino-ide-extension/src/browser/boards/boards-config.tsx index 33e07b42..dc5b7c96 100644 --- a/arduino-ide-extension/src/browser/boards/boards-config.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-config.tsx @@ -3,12 +3,16 @@ import { Event } from '@theia/core/lib/common/event'; import { notEmpty } from '@theia/core/lib/common/objects'; import { MaybePromise } from '@theia/core/lib/common/types'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; -import { Board, Port, AttachedBoardsChangeEvent, BoardWithPackage } from '../../common/protocol/boards-service'; +import { + Board, + Port, + AttachedBoardsChangeEvent, + BoardWithPackage, +} from '../../common/protocol/boards-service'; import { NotificationCenter } from '../notification-center'; import { BoardsServiceProvider } from './boards-service-provider'; export namespace BoardsConfig { - export interface Config { selectedBoard?: Board; selectedPort?: Port; @@ -28,18 +32,16 @@ export namespace BoardsConfig { showAllPorts: boolean; query: string; } - } export abstract class Item extends React.Component<{ - item: T, - label: string, - selected: boolean, - onClick: (item: T) => void, - missing?: boolean, - details?: string + item: T; + label: string; + selected: boolean; + onClick: (item: T) => void; + missing?: boolean; + details?: string; }> { - render(): React.ReactNode { const { selected, label, missing, details } = this.props; const classNames = ['item']; @@ -47,25 +49,36 @@ export abstract class Item extends React.Component<{ classNames.push('selected'); } if (missing === true) { - classNames.push('missing') + classNames.push('missing'); } - return
-
- {label} + return ( +
+
{label}
+ {!details ? '' :
{details}
} + {!selected ? ( + '' + ) : ( +
+ +
+ )}
- {!details ? '' :
{details}
} - {!selected ? '' :
} -
; + ); } protected onClick = () => { this.props.onClick(this.props.item); - } - + }; } -export class BoardsConfig extends React.Component { - +export class BoardsConfig extends React.Component< + BoardsConfig.Props, + BoardsConfig.State +> { protected toDispose = new DisposableCollection(); constructor(props: BoardsConfig.Props) { @@ -77,24 +90,51 @@ export class BoardsConfig extends React.Component port).filter(notEmpty)); + this.updatePorts( + this.props.boardsServiceProvider.availableBoards + .map(({ port }) => port) + .filter(notEmpty) + ); this.toDispose.pushAll([ - this.props.notificationCenter.onAttachedBoardsChanged(event => this.updatePorts(event.newState.ports, AttachedBoardsChangeEvent.diff(event).detached.ports)), - this.props.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard, selectedPort }) => { - this.setState({ selectedBoard, selectedPort }, () => this.fireConfigChanged()); - }), - this.props.notificationCenter.onPlatformInstalled(() => this.updateBoards(this.state.query)), - this.props.notificationCenter.onPlatformUninstalled(() => this.updateBoards(this.state.query)), - this.props.notificationCenter.onIndexUpdated(() => this.updateBoards(this.state.query)), - this.props.notificationCenter.onDaemonStarted(() => this.updateBoards(this.state.query)), - this.props.notificationCenter.onDaemonStopped(() => this.setState({ searchResults: [] })), - this.props.onFilteredTextDidChangeEvent(query => this.setState({ query }, () => this.updateBoards(this.state.query))) + this.props.notificationCenter.onAttachedBoardsChanged((event) => + this.updatePorts( + event.newState.ports, + AttachedBoardsChangeEvent.diff(event).detached.ports + ) + ), + this.props.boardsServiceProvider.onBoardsConfigChanged( + ({ selectedBoard, selectedPort }) => { + this.setState({ selectedBoard, selectedPort }, () => + this.fireConfigChanged() + ); + } + ), + this.props.notificationCenter.onPlatformInstalled(() => + this.updateBoards(this.state.query) + ), + this.props.notificationCenter.onPlatformUninstalled(() => + this.updateBoards(this.state.query) + ), + this.props.notificationCenter.onIndexUpdated(() => + this.updateBoards(this.state.query) + ), + this.props.notificationCenter.onDaemonStarted(() => + this.updateBoards(this.state.query) + ), + this.props.notificationCenter.onDaemonStopped(() => + this.setState({ searchResults: [] }) + ), + this.props.onFilteredTextDidChangeEvent((query) => + this.setState({ query }, () => + this.updateBoards(this.state.query) + ) + ), ]); } @@ -107,73 +147,96 @@ export class BoardsConfig extends React.Component | string = '') => { - const query = typeof eventOrQuery === 'string' - ? eventOrQuery - : eventOrQuery.target.value.toLowerCase(); + protected updateBoards = ( + eventOrQuery: React.ChangeEvent | string = '' + ) => { + const query = + typeof eventOrQuery === 'string' + ? eventOrQuery + : eventOrQuery.target.value.toLowerCase(); this.setState({ query }); - this.queryBoards({ query }).then(searchResults => this.setState({ searchResults })); - } + this.queryBoards({ query }).then((searchResults) => + this.setState({ searchResults }) + ); + }; protected updatePorts = (ports: Port[] = [], removedPorts: Port[] = []) => { this.queryPorts(Promise.resolve(ports)).then(({ knownPorts }) => { let { selectedPort } = this.state; // If the currently selected port is not available anymore, unset the selected port. - if (removedPorts.some(port => Port.equals(port, selectedPort))) { + if (removedPorts.some((port) => Port.equals(port, selectedPort))) { selectedPort = undefined; } - this.setState({ knownPorts, selectedPort }, () => this.fireConfigChanged()); + this.setState({ knownPorts, selectedPort }, () => + this.fireConfigChanged() + ); }); - } + }; - protected queryBoards = (options: { query?: string } = {}): Promise> => { + protected queryBoards = ( + options: { query?: string } = {} + ): Promise> => { return this.props.boardsServiceProvider.searchBoards(options); - } + }; protected get availablePorts(): MaybePromise { - return this.props.boardsServiceProvider.availableBoards.map(({ port }) => port).filter(notEmpty); + return this.props.boardsServiceProvider.availableBoards + .map(({ port }) => port) + .filter(notEmpty); } - protected queryPorts = async (availablePorts: MaybePromise = this.availablePorts) => { + protected queryPorts = async ( + availablePorts: MaybePromise = this.availablePorts + ) => { const ports = await availablePorts; return { knownPorts: ports.sort(Port.compare) }; - } + }; protected toggleFilterPorts = () => { this.setState({ showAllPorts: !this.state.showAllPorts }); - } + }; protected selectPort = (selectedPort: Port | undefined) => { this.setState({ selectedPort }, () => this.fireConfigChanged()); - } + }; protected selectBoard = (selectedBoard: BoardWithPackage | undefined) => { this.setState({ selectedBoard }, () => this.fireConfigChanged()); - } + }; protected focusNodeSet = (element: HTMLElement | null) => { this.props.onFocusNodeSet(element || undefined); - } + }; render(): React.ReactNode { - return
- {this.renderContainer('boards', this.renderBoards.bind(this))} - {this.renderContainer('ports', this.renderPorts.bind(this), this.renderPortsFooter.bind(this))} -
; + return ( +
+ {this.renderContainer('boards', this.renderBoards.bind(this))} + {this.renderContainer( + 'ports', + this.renderPorts.bind(this), + this.renderPortsFooter.bind(this) + )} +
+ ); } - protected renderContainer(title: string, contentRenderer: () => React.ReactNode, footerRenderer?: () => React.ReactNode): React.ReactNode { - return
-
-
- {title} -
- {contentRenderer()} -
- {(footerRenderer ? footerRenderer() : '')} + protected renderContainer( + title: string, + contentRenderer: () => React.ReactNode, + footerRenderer?: () => React.ReactNode + ): React.ReactNode { + return ( +
+
+
{title}
+ {contentRenderer()} +
+ {footerRenderer ? footerRenderer() : ''} +
-
; + ); } protected renderBoards(): React.ReactNode { @@ -181,98 +244,111 @@ export class BoardsConfig extends React.Component(); - const toKey = ({ name, packageName, fqbn }: Board.Detailed) => !!fqbn ? `${name}-${packageName}-${fqbn}` : `${name}-${packageName}`; - for (const board of Board.decorateBoards(selectedBoard, searchResults)) { + const toKey = ({ name, packageName, fqbn }: Board.Detailed) => + !!fqbn + ? `${name}-${packageName}-${fqbn}` + : `${name}-${packageName}`; + for (const board of Board.decorateBoards( + selectedBoard, + searchResults + )) { const key = toKey(board); if (!distinctBoards.has(key)) { distinctBoards.set(key, board); } } - return -
- - -
-
- {Array.from(distinctBoards.values()).map(board => - key={`${board.name}-${board.packageName}`} - item={board} - label={board.name} - details={board.details} - selected={board.selected} - onClick={this.selectBoard} - missing={board.missing} - />)} -
-
; + return ( + +
+ + +
+
+ {Array.from(distinctBoards.values()).map((board) => ( + + key={`${board.name}-${board.packageName}`} + item={board} + label={board.name} + details={board.details} + selected={board.selected} + onClick={this.selectBoard} + missing={board.missing} + /> + ))} +
+
+ ); } protected renderPorts(): React.ReactNode { const filter = this.state.showAllPorts ? () => true : Port.isBoardPort; const ports = this.state.knownPorts.filter(filter); - return !ports.length ? - ( -
- No ports discovered -
- ) : - ( -
- {ports.map(port => + return !ports.length ? ( +
No ports discovered
+ ) : ( +
+ {ports.map((port) => ( + key={Port.toString(port)} item={port} label={Port.toString(port)} selected={Port.equals(this.state.selectedPort, port)} onClick={this.selectPort} - />)} -
- ); + /> + ))} +
+ ); } protected renderPortsFooter(): React.ReactNode { - return
- -
; + return ( +
+ +
+ ); } - } export namespace BoardsConfig { - export namespace Config { - export function sameAs(config: Config, other: Config | Board): boolean { const { selectedBoard, selectedPort } = config; if (Board.is(other)) { - return !!selectedBoard - && Board.equals(other, selectedBoard) - && Port.sameAs(selectedPort, other.port); + return ( + !!selectedBoard && + Board.equals(other, selectedBoard) && + Port.sameAs(selectedPort, other.port) + ); } return sameAs(config, other); } export function equals(left: Config, right: Config): boolean { - return left.selectedBoard === right.selectedBoard - && left.selectedPort === right.selectedPort; + return ( + left.selectedBoard === right.selectedBoard && + left.selectedPort === right.selectedPort + ); } - export function toString(config: Config, options: { default: string } = { default: '' }): string { + export function toString( + config: Config, + options: { default: string } = { default: '' } + ): string { const { selectedBoard, selectedPort: port } = config; if (!selectedBoard) { return options.default; @@ -281,17 +357,33 @@ export namespace BoardsConfig { return `${name}${port ? ' at ' + Port.toString(port) : ''}`; } - export function setConfig(config: Config | undefined, urlToAttachTo: URL): URL { + export function setConfig( + config: Config | undefined, + urlToAttachTo: URL + ): URL { const copy = new URL(urlToAttachTo.toString()); if (!config) { copy.searchParams.delete('boards-config'); return copy; } - const selectedBoard = config.selectedBoard ? { name: config.selectedBoard.name, fqbn: config.selectedBoard.fqbn } : undefined; - const selectedPort = config.selectedPort ? { protocol: config.selectedPort.protocol, address: config.selectedPort.address } : undefined; + const selectedBoard = config.selectedBoard + ? { + name: config.selectedBoard.name, + fqbn: config.selectedBoard.fqbn, + } + : undefined; + const selectedPort = config.selectedPort + ? { + protocol: config.selectedPort.protocol, + address: config.selectedPort.address, + } + : undefined; const jsonConfig = JSON.stringify({ selectedBoard, selectedPort }); - copy.searchParams.set('boards-config', encodeURIComponent(jsonConfig)); + copy.searchParams.set( + 'boards-config', + encodeURIComponent(jsonConfig) + ); return copy; } @@ -306,14 +398,14 @@ export namespace BoardsConfig { if (typeof candidate === 'object') { return candidate; } - console.warn(`Expected candidate to be an object. It was ${typeof candidate}. URL was: ${url}`); + console.warn( + `Expected candidate to be an object. It was ${typeof candidate}. URL was: ${url}` + ); return undefined; } catch (e) { console.log(`Could not get board config from URL: ${url}.`, e); return undefined; } } - } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-data-menu-updater.ts b/arduino-ide-extension/src/browser/boards/boards-data-menu-updater.ts index ec517de9..a7beed9b 100644 --- a/arduino-ide-extension/src/browser/boards/boards-data-menu-updater.ts +++ b/arduino-ide-extension/src/browser/boards/boards-data-menu-updater.ts @@ -2,7 +2,10 @@ 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 { + 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'; @@ -12,7 +15,6 @@ import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus'; @injectable() export class BoardsDataMenuUpdater implements FrontendApplicationContribution { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -32,63 +34,153 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution { 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)); + 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 { + 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); + 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() + 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 + execute: () => + this.boardsDataStore.selectConfigOption( + { fqbn, option, selectedValue } + ), + isToggled: () => value.selected, }; - commands.set(id, Object.assign(this.commandRegistry.registerCommand(command, handler), { label: value.label })); + 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)); - }) + 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))); + 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) + 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.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)) + Disposable.create(() => + this.commandRegistry.unregisterCommand( + command + ) + ), + Disposable.create(() => + this.menuRegistry.unregisterMenuAction( + command.id + ) + ), ]); } } @@ -97,5 +189,4 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution { } }); } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-data-store.ts b/arduino-ide-extension/src/browser/boards/boards-data-store.ts index 12771c4a..4771e952 100644 --- a/arduino-ide-extension/src/browser/boards/boards-data-store.ts +++ b/arduino-ide-extension/src/browser/boards/boards-data-store.ts @@ -3,14 +3,22 @@ import { ILogger } from '@theia/core/lib/common/logger'; import { deepClone } from '@theia/core/lib/common/objects'; import { MaybePromise } from '@theia/core/lib/common/types'; import { Event, Emitter } from '@theia/core/lib/common/event'; -import { FrontendApplicationContribution, LocalStorageService } from '@theia/core/lib/browser'; +import { + FrontendApplicationContribution, + LocalStorageService, +} from '@theia/core/lib/browser'; import { notEmpty } from '../../common/utils'; -import { BoardsService, ConfigOption, Installable, BoardDetails, Programmer } from '../../common/protocol'; +import { + BoardsService, + ConfigOption, + Installable, + BoardDetails, + Programmer, +} from '../../common/protocol'; import { NotificationCenter } from '../notification-center'; @injectable() export class BoardsDataStore implements FrontendApplicationContribution { - @inject(ILogger) @named('store') protected readonly logger: ILogger; @@ -33,9 +41,14 @@ export class BoardsDataStore implements FrontendApplicationContribution { return; } let shouldFireChanged = false; - for (const fqbn of item.boards.map(({ fqbn }) => fqbn).filter(notEmpty).filter(fqbn => !!fqbn)) { + for (const fqbn of item.boards + .map(({ fqbn }) => fqbn) + .filter(notEmpty) + .filter((fqbn) => !!fqbn)) { const key = this.getStorageKey(fqbn, version); - let data = await this.storageService.getData(key); + let data = await this.storageService.getData< + ConfigOption[] | undefined + >(key); if (!data || !data.length) { const details = await this.getBoardDetailsSafe(fqbn); if (details) { @@ -59,20 +72,27 @@ export class BoardsDataStore implements FrontendApplicationContribution { async appendConfigToFqbn( fqbn: string | undefined, - boardsPackageVersion: MaybePromise = this.getBoardsPackageVersion(fqbn)): Promise { - + boardsPackageVersion: MaybePromise< + Installable.Version | undefined + > = this.getBoardsPackageVersion(fqbn) + ): Promise { if (!fqbn) { return undefined; } - const { configOptions } = await this.getData(fqbn, boardsPackageVersion); + const { configOptions } = await this.getData( + fqbn, + boardsPackageVersion + ); return ConfigOption.decorate(fqbn, configOptions); } async getData( fqbn: string | undefined, - boardsPackageVersion: MaybePromise = this.getBoardsPackageVersion(fqbn)): Promise { - + boardsPackageVersion: MaybePromise< + Installable.Version | undefined + > = this.getBoardsPackageVersion(fqbn) + ): Promise { if (!fqbn) { return BoardsDataStore.Data.EMPTY; } @@ -82,7 +102,9 @@ export class BoardsDataStore implements FrontendApplicationContribution { return BoardsDataStore.Data.EMPTY; } const key = this.getStorageKey(fqbn, version); - let data = await this.storageService.getData(key, undefined); + let data = await this.storageService.getData< + BoardsDataStore.Data | undefined + >(key, undefined); if (BoardsDataStore.Data.is(data)) { return data; } @@ -92,18 +114,28 @@ export class BoardsDataStore implements FrontendApplicationContribution { return BoardsDataStore.Data.EMPTY; } - data = { configOptions: boardDetails.configOptions, programmers: boardDetails.programmers }; + data = { + configOptions: boardDetails.configOptions, + programmers: boardDetails.programmers, + }; await this.storageService.setData(key, data); return data; } async selectProgrammer( - { fqbn, selectedProgrammer }: { fqbn: string, selectedProgrammer: Programmer }, - boardsPackageVersion: MaybePromise = this.getBoardsPackageVersion(fqbn)): Promise { - + { + fqbn, + selectedProgrammer, + }: { fqbn: string; selectedProgrammer: Programmer }, + boardsPackageVersion: MaybePromise< + Installable.Version | undefined + > = this.getBoardsPackageVersion(fqbn) + ): Promise { const data = deepClone(await this.getData(fqbn, boardsPackageVersion)); const { programmers } = data; - if (!programmers.find(p => Programmer.equals(selectedProgrammer, p))) { + if ( + !programmers.find((p) => Programmer.equals(selectedProgrammer, p)) + ) { return false; } @@ -112,18 +144,28 @@ export class BoardsDataStore implements FrontendApplicationContribution { return false; } - await this.setData({ fqbn, data: { ...data, selectedProgrammer }, version }); + await this.setData({ + fqbn, + data: { ...data, selectedProgrammer }, + version, + }); this.fireChanged(); return true; } async selectConfigOption( - { fqbn, option, selectedValue }: { fqbn: string, option: string, selectedValue: string }, - boardsPackageVersion: MaybePromise = this.getBoardsPackageVersion(fqbn)): Promise { - + { + fqbn, + option, + selectedValue, + }: { fqbn: string; option: string; selectedValue: string }, + boardsPackageVersion: MaybePromise< + Installable.Version | undefined + > = this.getBoardsPackageVersion(fqbn) + ): Promise { const data = deepClone(await this.getData(fqbn, boardsPackageVersion)); const { configOptions } = data; - const configOption = configOptions.find(c => c.option === option); + const configOption = configOptions.find((c) => c.option === option); if (!configOption) { return false; } @@ -149,26 +191,46 @@ export class BoardsDataStore implements FrontendApplicationContribution { return true; } - protected async setData( - { fqbn, data, version }: { fqbn: string, data: BoardsDataStore.Data, version: Installable.Version }): Promise { - + protected async setData({ + fqbn, + data, + version, + }: { + fqbn: string; + data: BoardsDataStore.Data; + version: Installable.Version; + }): Promise { const key = this.getStorageKey(fqbn, version); return this.storageService.setData(key, data); } - protected getStorageKey(fqbn: string, version: Installable.Version): string { + protected getStorageKey( + fqbn: string, + version: Installable.Version + ): string { return `.arduinoIDE-configOptions-${version}-${fqbn}`; } - protected async getBoardDetailsSafe(fqbn: string): Promise { + protected async getBoardDetailsSafe( + fqbn: string + ): Promise { try { const details = this.boardsService.getBoardDetails({ fqbn }); return details; } catch (err) { - if (err instanceof Error && err.message.includes('loading board data') && err.message.includes('is not installed')) { - this.logger.warn(`The boards package is not installed for board with FQBN: ${fqbn}`); + if ( + err instanceof Error && + err.message.includes('loading board data') && + err.message.includes('is not installed') + ) { + this.logger.warn( + `The boards package is not installed for board with FQBN: ${fqbn}` + ); } else { - this.logger.error(`An unexpected error occurred while retrieving the board details for ${fqbn}.`, err); + this.logger.error( + `An unexpected error occurred while retrieving the board details for ${fqbn}.`, + err + ); } return undefined; } @@ -178,17 +240,20 @@ export class BoardsDataStore implements FrontendApplicationContribution { this.onChangedEmitter.fire(); } - protected async getBoardsPackageVersion(fqbn: string | undefined): Promise { + protected async getBoardsPackageVersion( + fqbn: string | undefined + ): Promise { if (!fqbn) { return undefined; } - const boardsPackage = await this.boardsService.getContainerBoardPackage({ fqbn }); + const boardsPackage = await this.boardsService.getContainerBoardPackage( + { fqbn } + ); if (!boardsPackage) { return undefined; } return boardsPackage.installedVersion; } - } export namespace BoardsDataStore { @@ -200,12 +265,16 @@ export namespace BoardsDataStore { export namespace Data { export const EMPTY: Data = { configOptions: [], - programmers: [] + programmers: [], }; export function is(arg: any): arg is Data { - return !!arg - && 'configOptions' in arg && Array.isArray(arg['configOptions']) - && 'programmers' in arg && Array.isArray(arg['programmers']) + return ( + !!arg && + 'configOptions' in arg && + Array.isArray(arg['configOptions']) && + 'programmers' in arg && + Array.isArray(arg['programmers']) + ); } } } diff --git a/arduino-ide-extension/src/browser/boards/boards-list-widget.ts b/arduino-ide-extension/src/browser/boards/boards-list-widget.ts index 7ec54b29..fdb8408f 100644 --- a/arduino-ide-extension/src/browser/boards/boards-list-widget.ts +++ b/arduino-ide-extension/src/browser/boards/boards-list-widget.ts @@ -1,18 +1,21 @@ import { inject, injectable, postConstruct } from 'inversify'; -import { BoardsPackage, BoardsService } from '../../common/protocol/boards-service'; +import { + BoardsPackage, + BoardsService, +} from '../../common/protocol/boards-service'; import { ListWidget } from '../widgets/component-list/list-widget'; import { ListItemRenderer } from '../widgets/component-list/list-item-renderer'; @injectable() export class BoardsListWidget extends ListWidget { - static WIDGET_ID = 'boards-list-widget'; static WIDGET_LABEL = 'Boards Manager'; constructor( @inject(BoardsService) protected service: BoardsService, - @inject(ListItemRenderer) protected itemRenderer: ListItemRenderer) { - + @inject(ListItemRenderer) + protected itemRenderer: ListItemRenderer + ) { super({ id: BoardsListWidget.WIDGET_ID, label: BoardsListWidget.WIDGET_LABEL, @@ -21,7 +24,7 @@ export class BoardsListWidget extends ListWidget { installable: service, itemLabel: (item: BoardsPackage) => item.name, itemDeprecated: (item: BoardsPackage) => item.deprecated, - itemRenderer + itemRenderer, }); } @@ -29,19 +32,42 @@ export class BoardsListWidget extends ListWidget { protected init(): void { super.init(); this.toDispose.pushAll([ - this.notificationCenter.onPlatformInstalled(() => this.refresh(undefined)), - this.notificationCenter.onPlatformUninstalled(() => this.refresh(undefined)), + this.notificationCenter.onPlatformInstalled(() => + this.refresh(undefined) + ), + this.notificationCenter.onPlatformUninstalled(() => + this.refresh(undefined) + ), ]); } - protected async install({ item, progressId, version }: { item: BoardsPackage, progressId: string, version: string; }): Promise { + protected async install({ + item, + progressId, + version, + }: { + item: BoardsPackage; + progressId: string; + version: string; + }): Promise { await super.install({ item, progressId, version }); - this.messageService.info(`Successfully installed platform ${item.name}:${version}`, { timeout: 3000 }); + this.messageService.info( + `Successfully installed platform ${item.name}:${version}`, + { timeout: 3000 } + ); } - protected async uninstall({ item, progressId }: { item: BoardsPackage, progressId: string }): Promise { + protected async uninstall({ + item, + progressId, + }: { + item: BoardsPackage; + progressId: string; + }): Promise { await super.uninstall({ item, progressId }); - this.messageService.info(`Successfully uninstalled platform ${item.name}:${item.installedVersion}`, { timeout: 3000 }); + this.messageService.info( + `Successfully uninstalled platform ${item.name}:${item.installedVersion}`, + { timeout: 3000 } + ); } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts index b02e918d..d5a21609 100644 --- a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts +++ b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts @@ -11,7 +11,7 @@ import { BoardsService, BoardsPackage, AttachedBoardsChangeEvent, - BoardWithPackage + BoardWithPackage, } from '../../common/protocol'; import { BoardsConfig } from './boards-config'; import { naturalCompare } from '../../common/utils'; @@ -21,14 +21,12 @@ import { StorageWrapper } from '../storage-wrapper'; @injectable() export class BoardsServiceProvider implements FrontendApplicationContribution { - @inject(ILogger) protected logger: ILogger; @inject(MessageService) protected messageService: MessageService; - @inject(BoardsService) protected boardsService: BoardsService; @@ -38,8 +36,11 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { @inject(NotificationCenter) protected notificationCenter: NotificationCenter; - protected readonly onBoardsConfigChangedEmitter = new Emitter(); - protected readonly onAvailableBoardsChangedEmitter = new Emitter(); + protected readonly onBoardsConfigChangedEmitter = + new Emitter(); + protected readonly onAvailableBoardsChangedEmitter = new Emitter< + AvailableBoard[] + >(); /** * Used for the auto-reconnecting. Sometimes, the attached board gets disconnected after uploading something to it. @@ -48,7 +49,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { * We have to listen on such changes and auto-reconnect the same board on another port. * See: https://arduino.slack.com/archives/CJJHJCJSJ/p1568645417013000?thread_ts=1568640504.009400&cid=CJJHJCJSJ */ - protected latestValidBoardsConfig: RecursiveRequired | undefined = undefined; + protected latestValidBoardsConfig: + | RecursiveRequired + | undefined = undefined; protected latestBoardsConfig: BoardsConfig.Config | undefined = undefined; protected _boardsConfig: BoardsConfig.Config = {}; protected _attachedBoards: Board[] = []; // This does not contain the `Unknown` boards. They're visible from the available ports only. @@ -63,17 +66,24 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { * This even also emitted when the board package for the currently selected board was uninstalled. */ readonly onBoardsConfigChanged = this.onBoardsConfigChangedEmitter.event; - readonly onAvailableBoardsChanged = this.onAvailableBoardsChangedEmitter.event; + readonly onAvailableBoardsChanged = + this.onAvailableBoardsChangedEmitter.event; onStart(): void { - this.notificationCenter.onAttachedBoardsChanged(this.notifyAttachedBoardsChanged.bind(this)); - this.notificationCenter.onPlatformInstalled(this.notifyPlatformInstalled.bind(this)); - this.notificationCenter.onPlatformUninstalled(this.notifyPlatformUninstalled.bind(this)); + this.notificationCenter.onAttachedBoardsChanged( + this.notifyAttachedBoardsChanged.bind(this) + ); + this.notificationCenter.onPlatformInstalled( + this.notifyPlatformInstalled.bind(this) + ); + this.notificationCenter.onPlatformUninstalled( + this.notifyPlatformUninstalled.bind(this) + ); Promise.all([ this.boardsService.getAttachedBoards(), this.boardsService.getAvailablePorts(), - this.loadState() + this.loadState(), ]).then(([attachedBoards, availablePorts]) => { this._attachedBoards = attachedBoards; this._availablePorts = availablePorts; @@ -81,7 +91,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { }); } - protected notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void { + protected notifyAttachedBoardsChanged( + event: AttachedBoardsChangeEvent + ): void { if (!AttachedBoardsChangeEvent.isEmpty(event)) { this.logger.info('Attached boards and available ports changed:'); this.logger.info(AttachedBoardsChangeEvent.toString(event)); @@ -97,12 +109,20 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { const { selectedBoard } = this.boardsConfig; const { installedVersion, id } = event.item; if (selectedBoard) { - const installedBoard = event.item.boards.find(({ name }) => name === selectedBoard.name); - if (installedBoard && (!selectedBoard.fqbn || selectedBoard.fqbn === installedBoard.fqbn)) { - this.logger.info(`Board package ${id}[${installedVersion}] was installed. Updating the FQBN of the currently selected ${selectedBoard.name} board. [FQBN: ${installedBoard.fqbn}]`); + const installedBoard = event.item.boards.find( + ({ name }) => name === selectedBoard.name + ); + if ( + installedBoard && + (!selectedBoard.fqbn || + selectedBoard.fqbn === installedBoard.fqbn) + ) { + this.logger.info( + `Board package ${id}[${installedVersion}] was installed. Updating the FQBN of the currently selected ${selectedBoard.name} board. [FQBN: ${installedBoard.fqbn}]` + ); this.boardsConfig = { ...this.boardsConfig, - selectedBoard: installedBoard + selectedBoard: installedBoard, }; return; } @@ -110,13 +130,26 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { // This logic handles it "gracefully" by unselecting the board, so that we can avoid no FQBN is set error. // https://github.com/arduino/arduino-cli/issues/620 // https://github.com/arduino/arduino-pro-ide/issues/374 - if (BoardWithPackage.is(selectedBoard) && selectedBoard.packageId === event.item.id && !installedBoard) { - this.messageService.warn(`Could not find previously selected board '${selectedBoard.name}' in installed platform '${event.item.name}'. Please manually reselect the board you want to use. Do you want to reselect it now?`, 'Reselect later', 'Yes').then(async answer => { - if (answer === 'Yes') { - this.commandService.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id, selectedBoard.name); - } - }); - this.boardsConfig = {} + if ( + BoardWithPackage.is(selectedBoard) && + selectedBoard.packageId === event.item.id && + !installedBoard + ) { + this.messageService + .warn( + `Could not find previously selected board '${selectedBoard.name}' in installed platform '${event.item.name}'. Please manually reselect the board you want to use. Do you want to reselect it now?`, + 'Reselect later', + 'Yes' + ) + .then(async (answer) => { + if (answer === 'Yes') { + this.commandService.executeCommand( + ArduinoCommands.OPEN_BOARDS_DIALOG.id, + selectedBoard.name + ); + } + }); + this.boardsConfig = {}; return; } // Trigger a board re-set. See: https://github.com/arduino/arduino-cli/issues/954 @@ -129,8 +162,13 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { this.logger.info('Boards package uninstalled: ', JSON.stringify(event)); const { selectedBoard } = this.boardsConfig; if (selectedBoard && selectedBoard.fqbn) { - const uninstalledBoard = event.item.boards.find(({ name }) => name === selectedBoard.name); - if (uninstalledBoard && uninstalledBoard.fqbn === selectedBoard.fqbn) { + const uninstalledBoard = event.item.boards.find( + ({ name }) => name === selectedBoard.name + ); + if ( + uninstalledBoard && + uninstalledBoard.fqbn === selectedBoard.fqbn + ) { // We should not unset the FQBN, if the selected board is an attached, recognized board. // Attach Uno and install AVR, select Uno. Uninstall the AVR core while Uno is selected. We do not want to discard the FQBN of the Uno board. // Dev note: We cannot assume the `selectedBoard` is a type of `AvailableBoard`. @@ -138,43 +176,68 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { // it is just a FQBN, so we need to find the `selected` board among the `AvailableBoards` const selectedAvailableBoard = AvailableBoard.is(selectedBoard) ? selectedBoard - : this._availableBoards.find(availableBoard => Board.sameAs(availableBoard, selectedBoard)); - if (selectedAvailableBoard && selectedAvailableBoard.selected && selectedAvailableBoard.state === AvailableBoard.State.recognized) { + : this._availableBoards.find((availableBoard) => + Board.sameAs(availableBoard, selectedBoard) + ); + if ( + selectedAvailableBoard && + selectedAvailableBoard.selected && + selectedAvailableBoard.state === + AvailableBoard.State.recognized + ) { return; } - this.logger.info(`Board package ${event.item.id} was uninstalled. Discarding the FQBN of the currently selected ${selectedBoard.name} board.`); + this.logger.info( + `Board package ${event.item.id} was uninstalled. Discarding the FQBN of the currently selected ${selectedBoard.name} board.` + ); const selectedBoardWithoutFqbn = { - name: selectedBoard.name + name: selectedBoard.name, // No FQBN }; this.boardsConfig = { ...this.boardsConfig, - selectedBoard: selectedBoardWithoutFqbn + selectedBoard: selectedBoardWithoutFqbn, }; } } } protected async tryReconnect(): Promise { - if (this.latestValidBoardsConfig && !this.canUploadTo(this.boardsConfig)) { - for (const board of this.availableBoards.filter(({ state }) => state !== AvailableBoard.State.incomplete)) { - if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn - && this.latestValidBoardsConfig.selectedBoard.name === board.name - && Port.sameAs(this.latestValidBoardsConfig.selectedPort, board.port)) { - + if ( + this.latestValidBoardsConfig && + !this.canUploadTo(this.boardsConfig) + ) { + for (const board of this.availableBoards.filter( + ({ state }) => state !== AvailableBoard.State.incomplete + )) { + if ( + this.latestValidBoardsConfig.selectedBoard.fqbn === + board.fqbn && + this.latestValidBoardsConfig.selectedBoard.name === + board.name && + Port.sameAs( + this.latestValidBoardsConfig.selectedPort, + board.port + ) + ) { this.boardsConfig = this.latestValidBoardsConfig; return true; } } // If we could not find an exact match, we compare the board FQBN-name pairs and ignore the port, as it might have changed. // See documentation on `latestValidBoardsConfig`. - for (const board of this.availableBoards.filter(({ state }) => state !== AvailableBoard.State.incomplete)) { - if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn - && this.latestValidBoardsConfig.selectedBoard.name === board.name) { - + for (const board of this.availableBoards.filter( + ({ state }) => state !== AvailableBoard.State.incomplete + )) { + if ( + this.latestValidBoardsConfig.selectedBoard.fqbn === + board.fqbn && + this.latestValidBoardsConfig.selectedBoard.name === + board.name + ) { this.boardsConfig = { ...this.latestValidBoardsConfig, - selectedPort: board.port + selectedPort: board.port, }; return true; } @@ -185,7 +248,15 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { set boardsConfig(config: BoardsConfig.Config) { this.doSetBoardsConfig(config); - this.saveState().finally(() => this.reconcileAvailableBoards().finally(() => this.onBoardsConfigChangedEmitter.fire(this._boardsConfig))); + this.saveState().finally(() => + this.reconcileAvailableBoards().finally(() => + this.onBoardsConfigChangedEmitter.fire(this._boardsConfig) + ) + ); + } + + get boardsConfig(): BoardsConfig.Config { + return this._boardsConfig; } protected doSetBoardsConfig(config: BoardsConfig.Config): void { @@ -197,29 +268,33 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { } } - async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise { + async searchBoards({ + query, + cores, + }: { + query?: string; + cores?: string[]; + }): Promise { const boards = await this.boardsService.searchBoards({ query }); return boards; } - get boardsConfig(): BoardsConfig.Config { - return this._boardsConfig; - } - /** * `true` if the `config.selectedBoard` is defined; hence can compile against the board. Otherwise, `false`. */ canVerify( config: BoardsConfig.Config | undefined = this.boardsConfig, - options: { silent: boolean } = { silent: true }): config is BoardsConfig.Config & { selectedBoard: Board } { - + options: { silent: boolean } = { silent: true } + ): config is BoardsConfig.Config & { selectedBoard: Board } { if (!config) { return false; } if (!config.selectedBoard) { if (!options.silent) { - this.messageService.warn('No boards selected.', { timeout: 3000 }); + this.messageService.warn('No boards selected.', { + timeout: 3000, + }); } return false; } @@ -232,8 +307,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { */ canUploadTo( config: BoardsConfig.Config | undefined = this.boardsConfig, - options: { silent: boolean } = { silent: true }): config is RecursiveRequired { - + options: { silent: boolean } = { silent: true } + ): config is RecursiveRequired { if (!this.canVerify(config, options)) { return false; } @@ -241,14 +316,20 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { const { name } = config.selectedBoard; if (!config.selectedPort) { if (!options.silent) { - this.messageService.warn(`No ports selected for board: '${name}'.`, { timeout: 3000 }); + this.messageService.warn( + `No ports selected for board: '${name}'.`, + { timeout: 3000 } + ); } return false; } if (!config.selectedBoard.fqbn) { if (!options.silent) { - this.messageService.warn(`The FQBN is not available for the selected board ${name}. Do you have the corresponding core installed?`, { timeout: 3000 }); + this.messageService.warn( + `The FQBN is not available for the selected board ${name}. Do you have the corresponding core installed?`, + { timeout: 3000 } + ); } return false; } @@ -260,25 +341,46 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { return this._availableBoards; } - async waitUntilAvailable(what: Board & { port: Port }, timeout?: number): Promise { - const find = (needle: Board & { port: Port }, haystack: AvailableBoard[]) => - haystack.find(board => Board.equals(needle, board) && Port.equals(needle.port, board.port)); - const timeoutTask = !!timeout && timeout > 0 - ? new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${timeout} ms.`)), timeout)) - : new Promise(() => { /* never */ }); - const waitUntilTask = new Promise(resolve => { + async waitUntilAvailable( + what: Board & { port: Port }, + timeout?: number + ): Promise { + const find = ( + needle: Board & { port: Port }, + haystack: AvailableBoard[] + ) => + haystack.find( + (board) => + Board.equals(needle, board) && + Port.equals(needle.port, board.port) + ); + const timeoutTask = + !!timeout && timeout > 0 + ? new Promise((_, reject) => + setTimeout( + () => + reject(new Error(`Timeout after ${timeout} ms.`)), + timeout + ) + ) + : new Promise(() => { + /* never */ + }); + const waitUntilTask = new Promise((resolve) => { let candidate = find(what, this.availableBoards); if (candidate) { resolve(); return; } - const disposable = this.onAvailableBoardsChanged(availableBoards => { - candidate = find(what, availableBoards); - if (candidate) { - disposable.dispose(); - resolve(); + const disposable = this.onAvailableBoardsChanged( + (availableBoards) => { + candidate = find(what, availableBoards); + if (candidate) { + disposable.dispose(); + resolve(); + } } - }); + ); }); return await Promise.race([waitUntilTask, timeoutTask]); } @@ -287,54 +389,90 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { const attachedBoards = this._attachedBoards; const availablePorts = this._availablePorts; // Unset the port on the user's config, if it is not available anymore. - if (this.boardsConfig.selectedPort && !availablePorts.some(port => Port.sameAs(port, this.boardsConfig.selectedPort))) { - this.doSetBoardsConfig({ selectedBoard: this.boardsConfig.selectedBoard, selectedPort: undefined }); + if ( + this.boardsConfig.selectedPort && + !availablePorts.some((port) => + Port.sameAs(port, this.boardsConfig.selectedPort) + ) + ) { + this.doSetBoardsConfig({ + selectedBoard: this.boardsConfig.selectedBoard, + selectedPort: undefined, + }); this.onBoardsConfigChangedEmitter.fire(this._boardsConfig); } const boardsConfig = this.boardsConfig; const currentAvailableBoards = this._availableBoards; const availableBoards: AvailableBoard[] = []; const availableBoardPorts = availablePorts.filter(Port.isBoardPort); - const attachedSerialBoards = attachedBoards.filter(({ port }) => !!port); + const attachedSerialBoards = attachedBoards.filter( + ({ port }) => !!port + ); for (const boardPort of availableBoardPorts) { let state = AvailableBoard.State.incomplete; // Initial pessimism. - let board = attachedSerialBoards.find(({ port }) => Port.sameAs(boardPort, port)); + let board = attachedSerialBoards.find(({ port }) => + Port.sameAs(boardPort, port) + ); if (board) { state = AvailableBoard.State.recognized; } else { // If the selected board is not recognized because it is a 3rd party board: https://github.com/arduino/arduino-cli/issues/623 // We still want to show it without the red X in the boards toolbar: https://github.com/arduino/arduino-pro-ide/issues/198#issuecomment-599355836 - const lastSelectedBoard = await this.getLastSelectedBoardOnPort(boardPort); + const lastSelectedBoard = await this.getLastSelectedBoardOnPort( + boardPort + ); if (lastSelectedBoard) { board = { ...lastSelectedBoard, - port: boardPort + port: boardPort, }; state = AvailableBoard.State.guessed; } } if (!board) { - availableBoards.push({ name: 'Unknown', port: boardPort, state }); + availableBoards.push({ + name: 'Unknown', + port: boardPort, + state, + }); } else { - const selected = BoardsConfig.Config.sameAs(boardsConfig, board); - availableBoards.push({ ...board, state, selected, port: boardPort }); + const selected = BoardsConfig.Config.sameAs( + boardsConfig, + board + ); + availableBoards.push({ + ...board, + state, + selected, + port: boardPort, + }); } } - if (boardsConfig.selectedBoard && !availableBoards.some(({ selected }) => selected)) { + if ( + boardsConfig.selectedBoard && + !availableBoards.some(({ selected }) => selected) + ) { availableBoards.push({ ...boardsConfig.selectedBoard, port: boardsConfig.selectedPort, selected: true, - state: AvailableBoard.State.incomplete + state: AvailableBoard.State.incomplete, }); } - const sortedAvailableBoards = availableBoards.sort(AvailableBoard.compare); - let hasChanged = sortedAvailableBoards.length !== currentAvailableBoards.length; + const sortedAvailableBoards = availableBoards.sort( + AvailableBoard.compare + ); + let hasChanged = + sortedAvailableBoards.length !== currentAvailableBoards.length; for (let i = 0; !hasChanged && i < sortedAvailableBoards.length; i++) { - hasChanged = AvailableBoard.compare(sortedAvailableBoards[i], currentAvailableBoards[i]) !== 0; + hasChanged = + AvailableBoard.compare( + sortedAvailableBoards[i], + currentAvailableBoards[i] + ) !== 0; } if (hasChanged) { this._availableBoards = sortedAvailableBoards; @@ -342,7 +480,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { } } - protected async getLastSelectedBoardOnPort(port: Port | string | undefined): Promise { + protected async getLastSelectedBoardOnPort( + port: Port | string | undefined + ): Promise { if (!port) { return undefined; } @@ -361,18 +501,25 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { await this.setData(key, selectedBoard); } await Promise.all([ - this.setData('latest-valid-boards-config', this.latestValidBoardsConfig), - this.setData('latest-boards-config', this.latestBoardsConfig) + this.setData( + 'latest-valid-boards-config', + this.latestValidBoardsConfig + ), + this.setData('latest-boards-config', this.latestBoardsConfig), ]); } protected getLastSelectedBoardOnPortKey(port: Port | string): string { // TODO: we lose the port's `protocol` info (`serial`, `network`, etc.) here if the `port` is a `string`. - return `last-selected-board-on-port:${typeof port === 'string' ? port : Port.toString(port)}`; + return `last-selected-board-on-port:${ + typeof port === 'string' ? port : Port.toString(port) + }`; } protected async loadState(): Promise { - const storedLatestValidBoardsConfig = await this.getData>('latest-valid-boards-config'); + const storedLatestValidBoardsConfig = await this.getData< + RecursiveRequired + >('latest-valid-boards-config'); if (storedLatestValidBoardsConfig) { this.latestValidBoardsConfig = storedLatestValidBoardsConfig; if (this.canUploadTo(this.latestValidBoardsConfig)) { @@ -380,10 +527,14 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { } } else { // If we could not restore the latest valid config, try to restore something, the board at least. - let storedLatestBoardsConfig = await this.getData('latest-boards-config'); + let storedLatestBoardsConfig = await this.getData< + BoardsConfig.Config | undefined + >('latest-boards-config'); // Try to get from the URL if it was not persisted. if (!storedLatestBoardsConfig) { - storedLatestBoardsConfig = BoardsConfig.Config.getConfig(new URL(window.location.href)); + storedLatestBoardsConfig = BoardsConfig.Config.getConfig( + new URL(window.location.href) + ); } if (storedLatestBoardsConfig) { this.latestBoardsConfig = storedLatestBoardsConfig; @@ -393,11 +544,18 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { } private setData(key: string, value: T): Promise { - return this.commandService.executeCommand(StorageWrapper.Commands.SET_DATA.id, key, value); + return this.commandService.executeCommand( + StorageWrapper.Commands.SET_DATA.id, + key, + value + ); } private getData(key: string): Promise { - return this.commandService.executeCommand(StorageWrapper.Commands.GET_DATA.id, key); + return this.commandService.executeCommand( + StorageWrapper.Commands.GET_DATA.id, + key + ); } } @@ -414,7 +572,6 @@ export interface AvailableBoard extends Board { } export namespace AvailableBoard { - export enum State { /** * Retrieved from the CLI via the `board list` command. @@ -427,14 +584,16 @@ export namespace AvailableBoard { /** * We do not know anything about this board, probably a 3rd party. The user has not selected a board for this port yet. */ - 'incomplete' + 'incomplete', } export function is(board: any): board is AvailableBoard { return Board.is(board) && 'state' in board; } - export function hasPort(board: AvailableBoard): board is AvailableBoard & { port: Port } { + export function hasPort( + board: AvailableBoard + ): board is AvailableBoard & { port: Port } { return !!board.port; } @@ -468,6 +627,5 @@ export namespace AvailableBoard { return 1; } return left.state - right.state; - } - + }; } diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx index 8666926a..5c83a74d 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -5,7 +5,10 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { Port } from '../../common/protocol'; import { BoardsConfig } from './boards-config'; import { ArduinoCommands } from '../arduino-commands'; -import { BoardsServiceProvider, AvailableBoard } from './boards-service-provider'; +import { + BoardsServiceProvider, + AvailableBoard, +} from './boards-service-provider'; export interface BoardsDropDownListCoords { readonly top: number; @@ -17,13 +20,14 @@ export interface BoardsDropDownListCoords { export namespace BoardsDropDown { export interface Props { readonly coords: BoardsDropDownListCoords | 'hidden'; - readonly items: Array void, port: Port }>; + readonly items: Array< + AvailableBoard & { onClick: () => void; port: Port } + >; readonly openBoardsConfig: () => void; } } export class BoardsDropDown extends React.Component { - protected dropdownElement: HTMLElement; constructor(props: BoardsDropDown.Props) { @@ -47,35 +51,61 @@ export class BoardsDropDown extends React.Component { if (coords === 'hidden') { return ''; } - return
- {this.renderItem({ - label: 'Select Other Board & Port', - onClick: () => this.props.openBoardsConfig() - })} - {items.map(({ name, port, selected, onClick }) => ({ label: `${name} at ${Port.toString(port)}`, selected, onClick })).map(this.renderItem)} -
- } - - protected renderItem({ label, selected, onClick }: { label: string, selected?: boolean, onClick: () => void }): React.ReactNode { - return
-
- {label} + return ( +
+ {this.renderItem({ + label: 'Select Other Board & Port', + onClick: () => this.props.openBoardsConfig(), + })} + {items + .map(({ name, port, selected, onClick }) => ({ + label: `${name} at ${Port.toString(port)}`, + selected, + onClick, + })) + .map(this.renderItem)}
- {selected ? : ''} -
+ ); } + protected renderItem({ + label, + selected, + onClick, + }: { + label: string; + selected?: boolean; + onClick: () => void; + }): React.ReactNode { + return ( +
+
{label}
+ {selected ? : ''} +
+ ); + } } -export class BoardsToolBarItem extends React.Component { - +export class BoardsToolBarItem extends React.Component< + BoardsToolBarItem.Props, + BoardsToolBarItem.State +> { static TOOLBAR_ID: 'boards-toolbar'; - protected readonly toDispose: DisposableCollection = new DisposableCollection(); + protected readonly toDispose: DisposableCollection = + new DisposableCollection(); constructor(props: BoardsToolBarItem.Props) { super(props); @@ -83,7 +113,7 @@ export class BoardsToolBarItem extends React.Component { @@ -92,7 +122,9 @@ export class BoardsToolBarItem extends React.Component this.setState({ availableBoards })); + this.props.boardsServiceClient.onAvailableBoardsChanged( + (availableBoards) => this.setState({ availableBoards }) + ); } componentWillUnmount(): void { @@ -109,8 +141,8 @@ export class BoardsToolBarItem extends React.Component { - const selectedBoard = availableBoards.find(({ selected }) => selected); + const selectedBoard = availableBoards.find( + ({ selected }) => selected + ); if (!selectedBoard || !selectedBoard.port) { - return 'fa fa-times notAttached' + return 'fa fa-times notAttached'; } if (selectedBoard.state === AvailableBoard.State.guessed) { - return 'fa fa-exclamation-triangle guessed' + return 'fa fa-exclamation-triangle guessed'; } - return '' + return ''; })(); - return -
-
-
- -
-
- {title} + return ( + +
+
+
+ +
+
+ {title} +
+
-
-
- ({ - ...board, - onClick: () => { - if (board.state === AvailableBoard.State.incomplete) { - this.props.boardsServiceClient.boardsConfig = { - selectedPort: board.port - }; - this.openDialog(); - } else { - this.props.boardsServiceClient.boardsConfig = { - selectedBoard: board, - selectedPort: board.port - } - } - } - }))} - openBoardsConfig={this.openDialog}> - - ; + ({ + ...board, + onClick: () => { + if ( + board.state === + AvailableBoard.State.incomplete + ) { + this.props.boardsServiceClient.boardsConfig = + { + selectedPort: board.port, + }; + this.openDialog(); + } else { + this.props.boardsServiceClient.boardsConfig = + { + selectedBoard: board, + selectedPort: board.port, + }; + } + }, + }))} + openBoardsConfig={this.openDialog} + > + + ); } protected openDialog = () => { - this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id); + this.props.commands.executeCommand( + ArduinoCommands.OPEN_BOARDS_DIALOG.id + ); this.setState({ coords: 'hidden' }); }; - } export namespace BoardsToolBarItem { - export interface Props { readonly boardsServiceClient: BoardsServiceProvider; readonly commands: CommandRegistry; @@ -189,5 +234,4 @@ export namespace BoardsToolBarItem { availableBoards: AvailableBoard[]; coords: BoardsDropDownListCoords | 'hidden'; } - } diff --git a/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts b/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts index 47403845..bc344623 100644 --- a/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts @@ -5,22 +5,20 @@ import { ListWidgetFrontendContribution } from '../widgets/component-list/list-w @injectable() export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution { - constructor() { super({ widgetId: BoardsListWidget.WIDGET_ID, widgetName: BoardsListWidget.WIDGET_LABEL, defaultWidgetOptions: { area: 'left', - rank: 2 + rank: 2, }, toggleCommandId: `${BoardsListWidget.WIDGET_ID}:toggle`, - toggleKeybinding: 'CtrlCmd+Shift+B' + toggleKeybinding: 'CtrlCmd+Shift+B', }); } async initializeLayout(): Promise { this.openView(); } - } diff --git a/arduino-ide-extension/src/browser/contributions/about.ts b/arduino-ide-extension/src/browser/contributions/about.ts index de9a4733..96753eb8 100644 --- a/arduino-ide-extension/src/browser/contributions/about.ts +++ b/arduino-ide-extension/src/browser/contributions/about.ts @@ -4,13 +4,17 @@ import { remote } from 'electron'; import { isOSX, isWindows } from '@theia/core/lib/common/os'; import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; -import { Contribution, Command, MenuModelRegistry, CommandRegistry } from './contribution'; +import { + Contribution, + Command, + MenuModelRegistry, + CommandRegistry, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ConfigService } from '../../common/protocol'; @injectable() export class About extends Contribution { - @inject(ClipboardService) protected readonly clipboardService: ClipboardService; @@ -19,7 +23,7 @@ export class About extends Contribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(About.Commands.ABOUT_APP, { - execute: () => this.showAbout() + execute: () => this.showAbout(), }); } @@ -27,15 +31,23 @@ export class About extends Contribution { registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, { commandId: About.Commands.ABOUT_APP.id, label: `About ${this.applicationName}`, - order: '0' + order: '0', }); } async showAbout(): Promise { - const { version, commit, status: cliStatus } = await this.configService.getVersion(); + const { + version, + commit, + status: cliStatus, + } = await this.configService.getVersion(); const buildDate = this.buildDate; - const detail = (showAll: boolean) => `Version: ${remote.app.getVersion()} -Date: ${buildDate ? buildDate : 'dev build'}${buildDate && showAll ? ` (${this.ago(buildDate)})` : ''} + const detail = ( + showAll: boolean + ) => `Version: ${remote.app.getVersion()} +Date: ${buildDate ? buildDate : 'dev build'}${ + buildDate && showAll ? ` (${this.ago(buildDate)})` : '' + } CLI Version: ${version}${cliStatus ? ` ${cliStatus}` : ''} [${commit}] ${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''} @@ -43,16 +55,19 @@ ${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''} const ok = 'OK'; const copy = 'Copy'; const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy]; - const { response } = await remote.dialog.showMessageBox(remote.getCurrentWindow(), { - message: `${this.applicationName}`, - title: `${this.applicationName}`, - type: 'info', - detail: detail(true), - buttons, - noLink: true, - defaultId: buttons.indexOf(ok), - cancelId: buttons.indexOf(ok) - }); + const { response } = await remote.dialog.showMessageBox( + remote.getCurrentWindow(), + { + message: `${this.applicationName}`, + title: `${this.applicationName}`, + type: 'info', + detail: detail(true), + buttons, + noLink: true, + defaultId: buttons.indexOf(ok), + cancelId: buttons.indexOf(ok), + } + ); if (buttons[response] === copy) { await this.clipboardService.writeText(detail(false).trim()); @@ -72,7 +87,9 @@ ${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''} const other = moment(isoTime); let result = now.diff(other, 'minute'); if (result < 60) { - return result === 1 ? `${result} minute ago` : `${result} minute ago`; + return result === 1 + ? `${result} minute ago` + : `${result} minute ago`; } result = now.diff(other, 'hour'); if (result < 25) { @@ -88,18 +105,19 @@ ${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''} } result = now.diff(other, 'month'); if (result < 13) { - return result === 1 ? `${result} month ago` : `${result} months ago`; + return result === 1 + ? `${result} month ago` + : `${result} months ago`; } result = now.diff(other, 'year'); return result === 1 ? `${result} year ago` : `${result} years ago`; } - } export namespace About { export namespace Commands { export const ABOUT_APP: Command = { - id: 'arduino-about' + id: 'arduino-about', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/add-file.ts b/arduino-ide-extension/src/browser/contributions/add-file.ts index f9caf195..75b742e5 100644 --- a/arduino-ide-extension/src/browser/contributions/add-file.ts +++ b/arduino-ide-extension/src/browser/contributions/add-file.ts @@ -1,18 +1,23 @@ import { inject, injectable } from 'inversify'; import { remote } from 'electron'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, URI } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + URI, +} from './contribution'; import { FileDialogService } from '@theia/filesystem/lib/browser'; @injectable() export class AddFile extends SketchContribution { - @inject(FileDialogService) protected readonly fileDialogService: FileDialogService; registerCommands(registry: CommandRegistry): void { registry.registerCommand(AddFile.Commands.ADD_FILE, { - execute: () => this.addFile() + execute: () => this.addFile(), }); } @@ -20,7 +25,7 @@ export class AddFile extends SketchContribution { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { commandId: AddFile.Commands.ADD_FILE.id, label: 'Add File...', - order: '2' + order: '2', }); } @@ -33,7 +38,7 @@ export class AddFile extends SketchContribution { title: 'Add File', canSelectFiles: true, canSelectFolders: false, - canSelectMany: false + canSelectMany: false, }); if (!toAddUri) { return; @@ -47,22 +52,24 @@ export class AddFile extends SketchContribution { type: 'question', title: 'Replace', buttons: ['Cancel', 'OK'], - message: `Replace the existing version of ${filename}?` + message: `Replace the existing version of ${filename}?`, }); - if (response === 0) { // Cancel + if (response === 0) { + // Cancel return; } } await this.fileService.copy(toAddUri, targetUri, { overwrite: true }); - this.messageService.info('One file added to the sketch.', { timeout: 2000 }); + this.messageService.info('One file added to the sketch.', { + timeout: 2000, + }); } - } export namespace AddFile { export namespace Commands { export const ADD_FILE: Command = { - id: 'arduino-add-file' + id: 'arduino-add-file', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/add-zip-library.ts b/arduino-ide-extension/src/browser/contributions/add-zip-library.ts index d0845629..5ba58119 100644 --- a/arduino-ide-extension/src/browser/contributions/add-zip-library.ts +++ b/arduino-ide-extension/src/browser/contributions/add-zip-library.ts @@ -6,11 +6,15 @@ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ResponseServiceImpl } from '../response-service-impl'; import { Installable, LibraryService } from '../../common/protocol'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, +} from './contribution'; @injectable() export class AddZipLibrary extends SketchContribution { - @inject(EnvVariablesServer) protected readonly envVariableServer: EnvVariablesServer; @@ -22,18 +26,23 @@ export class AddZipLibrary extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, { - execute: () => this.addZipLibrary() + execute: () => this.addZipLibrary(), }); } registerMenus(registry: MenuModelRegistry): void { - const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include']; + const includeLibMenuPath = [ + ...ArduinoMenus.SKETCH__UTILS_GROUP, + '0_include', + ]; // TODO: do we need it? calling `registerSubmenu` multiple times is noop, so it does not hurt. - registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' }); + registry.registerSubmenu(includeLibMenuPath, 'Include Library', { + order: '1', + }); registry.registerMenuAction([...includeLibMenuPath, '1_install'], { commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id, label: 'Add .ZIP Library...', - order: '1' + order: '1', }); } @@ -47,9 +56,9 @@ export class AddZipLibrary extends SketchContribution { filters: [ { name: 'Library', - extensions: ['zip'] - } - ] + extensions: ['zip'], + }, + ], }); if (!canceled && filePaths.length) { const zipUri = await this.fileSystemExt.getUri(filePaths[0]); @@ -61,7 +70,7 @@ export class AddZipLibrary extends SketchContribution { msg: error.message, title: 'Do you want to overwrite the existing library?', ok: 'Yes', - cancel: 'No' + cancel: 'No', }).open(); if (result) { await this.doInstall(zipUri, true); @@ -71,24 +80,40 @@ export class AddZipLibrary extends SketchContribution { } } - private async doInstall(zipUri: string, overwrite?: boolean): Promise { + private async doInstall( + zipUri: string, + overwrite?: boolean + ): Promise { try { await Installable.doWithProgress({ messageService: this.messageService, progressText: `Processing ${new URI(zipUri).path.base}`, responseService: this.responseService, - run: () => this.libraryService.installZip({ zipUri, overwrite }) + run: () => + this.libraryService.installZip({ zipUri, overwrite }), }); - this.messageService.info(`Successfully installed library from ${new URI(zipUri).path.base} archive`, { timeout: 3000 }); + this.messageService.info( + `Successfully installed library from ${ + new URI(zipUri).path.base + } archive`, + { timeout: 3000 } + ); } catch (error) { if (error instanceof Error) { - const match = error.message.match(/library (.*?) already installed/); + const match = error.message.match( + /library (.*?) already installed/ + ); if (match && match.length >= 2) { const name = match[1].trim(); if (name) { - throw new AlreadyInstalledError(`A library folder named ${name} already exists. Do you want to overwrite it?`, name); + throw new AlreadyInstalledError( + `A library folder named ${name} already exists. Do you want to overwrite it?`, + name + ); } else { - throw new AlreadyInstalledError('A library already exists. Do you want to overwrite it?'); + throw new AlreadyInstalledError( + 'A library already exists. Do you want to overwrite it?' + ); } } } @@ -96,22 +121,19 @@ export class AddZipLibrary extends SketchContribution { throw error; } } - } class AlreadyInstalledError extends Error { - constructor(message: string, readonly libraryName?: string) { super(message); Object.setPrototypeOf(this, AlreadyInstalledError.prototype); } - } export namespace AddZipLibrary { export namespace Commands { export const ADD_ZIP_LIBRARY: Command = { - id: 'arduino-add-zip-library' + id: 'arduino-add-zip-library', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/archive-sketch.ts b/arduino-ide-extension/src/browser/contributions/archive-sketch.ts index d4f8da0d..95a6145e 100644 --- a/arduino-ide-extension/src/browser/contributions/archive-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/archive-sketch.ts @@ -3,14 +3,18 @@ import { remote } from 'electron'; import * as dateFormat from 'dateformat'; import URI from '@theia/core/lib/common/uri'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, +} from './contribution'; @injectable() export class ArchiveSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { registry.registerCommand(ArchiveSketch.Commands.ARCHIVE_SKETCH, { - execute: () => this.archiveSketch() + execute: () => this.archiveSketch(), }); } @@ -18,21 +22,29 @@ export class ArchiveSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id, label: 'Archive Sketch', - order: '1' + order: '1', }); } protected async archiveSketch(): Promise { const [sketch, config] = await Promise.all([ this.sketchServiceClient.currentSketch(), - this.configService.getConfiguration() + this.configService.getConfiguration(), ]); if (!sketch) { return; } - const archiveBasename = `${sketch.name}-${dateFormat(new Date(), 'yymmdd')}a.zip`; - const defaultPath = await this.fileService.fsPath(new URI(config.sketchDirUri).resolve(archiveBasename)); - const { filePath, canceled } = await remote.dialog.showSaveDialog({ title: 'Save sketch folder as...', defaultPath }); + const archiveBasename = `${sketch.name}-${dateFormat( + new Date(), + 'yymmdd' + )}a.zip`; + const defaultPath = await this.fileService.fsPath( + new URI(config.sketchDirUri).resolve(archiveBasename) + ); + const { filePath, canceled } = await remote.dialog.showSaveDialog({ + title: 'Save sketch folder as...', + defaultPath, + }); if (!filePath || canceled) { return; } @@ -41,15 +53,16 @@ export class ArchiveSketch extends SketchContribution { return; } await this.sketchService.archive(sketch, destinationUri.toString()); - this.messageService.info(`Created archive '${archiveBasename}'.`, { timeout: 2000 }); + this.messageService.info(`Created archive '${archiveBasename}'.`, { + timeout: 2000, + }); } - } export namespace ArchiveSketch { export namespace Commands { export const ARCHIVE_SKETCH: Command = { - id: 'arduino-archive-sketch' + id: 'arduino-archive-sketch', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/board-selection.ts b/arduino-ide-extension/src/browser/contributions/board-selection.ts index 3e84a76d..0b89ffaf 100644 --- a/arduino-ide-extension/src/browser/contributions/board-selection.ts +++ b/arduino-ide-extension/src/browser/contributions/board-selection.ts @@ -1,20 +1,31 @@ import { inject, injectable } from 'inversify'; import { remote } from 'electron'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; -import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; +import { + DisposableCollection, + Disposable, +} from '@theia/core/lib/common/disposable'; import { firstToUpperCase } from '../../common/utils'; import { BoardsConfig } from '../boards/boards-config'; import { MainMenuManager } from '../../common/main-menu-manager'; import { BoardsListWidget } from '../boards/boards-list-widget'; import { NotificationCenter } from '../notification-center'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { ArduinoMenus, PlaceholderMenuNode, unregisterSubmenu } from '../menu/arduino-menus'; -import { BoardsService, InstalledBoardWithPackage, AvailablePorts, Port } from '../../common/protocol'; +import { + ArduinoMenus, + PlaceholderMenuNode, + unregisterSubmenu, +} from '../menu/arduino-menus'; +import { + BoardsService, + InstalledBoardWithPackage, + AvailablePorts, + Port, +} from '../../common/protocol'; import { SketchContribution, Command, CommandRegistry } from './contribution'; @injectable() export class BoardSelection extends SketchContribution { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -38,81 +49,138 @@ export class BoardSelection extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, { execute: async () => { - const { selectedBoard, selectedPort } = this.boardsServiceProvider.boardsConfig; + const { selectedBoard, selectedPort } = + this.boardsServiceProvider.boardsConfig; if (!selectedBoard) { - this.messageService.info('Please select a board to obtain board info.'); + this.messageService.info( + 'Please select a board to obtain board info.' + ); return; } if (!selectedBoard.fqbn) { - this.messageService.info(`The platform for the selected '${selectedBoard.name}' board is not installed.`); + this.messageService.info( + `The platform for the selected '${selectedBoard.name}' board is not installed.` + ); return; } if (!selectedPort) { - this.messageService.info('Please select a port to obtain board info.'); + this.messageService.info( + 'Please select a port to obtain board info.' + ); return; } - const boardDetails = await this.boardsService.getBoardDetails({ fqbn: selectedBoard.fqbn }); + const boardDetails = await this.boardsService.getBoardDetails({ + fqbn: selectedBoard.fqbn, + }); if (boardDetails) { const { VID, PID } = boardDetails; const detail = `BN: ${selectedBoard.name} VID: ${VID} PID: ${PID}`; - await remote.dialog.showMessageBox(remote.getCurrentWindow(), { - message: 'Board Info', - title: 'Board Info', - type: 'info', - detail, - buttons: ['OK'] - }); + await remote.dialog.showMessageBox( + remote.getCurrentWindow(), + { + message: 'Board Info', + title: 'Board Info', + type: 'info', + detail, + buttons: ['OK'], + } + ); } - } + }, }); } onStart(): void { this.updateMenus(); - this.notificationCenter.onPlatformInstalled(this.updateMenus.bind(this)); - this.notificationCenter.onPlatformUninstalled(this.updateMenus.bind(this)); - this.boardsServiceProvider.onBoardsConfigChanged(this.updateMenus.bind(this)); - this.boardsServiceProvider.onAvailableBoardsChanged(this.updateMenus.bind(this)); + this.notificationCenter.onPlatformInstalled( + this.updateMenus.bind(this) + ); + this.notificationCenter.onPlatformUninstalled( + this.updateMenus.bind(this) + ); + this.boardsServiceProvider.onBoardsConfigChanged( + this.updateMenus.bind(this) + ); + this.boardsServiceProvider.onAvailableBoardsChanged( + this.updateMenus.bind(this) + ); } protected async updateMenus(): Promise { const [installedBoards, availablePorts, config] = await Promise.all([ this.installedBoards(), this.boardsService.getState(), - this.boardsServiceProvider.boardsConfig + this.boardsServiceProvider.boardsConfig, ]); this.rebuildMenus(installedBoards, availablePorts, config); } - protected rebuildMenus(installedBoards: InstalledBoardWithPackage[], availablePorts: AvailablePorts, config: BoardsConfig.Config): void { + protected rebuildMenus( + installedBoards: InstalledBoardWithPackage[], + availablePorts: AvailablePorts, + config: BoardsConfig.Config + ): void { this.toDisposeBeforeMenuRebuild.dispose(); // Boards submenu - const boardsSubmenuPath = [...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, '1_boards']; + const boardsSubmenuPath = [ + ...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, + '1_boards', + ]; const boardsSubmenuLabel = config.selectedBoard?.name; // Note: The submenu order starts from `100` because `Auto Format`, `Serial Monitor`, etc starts from `0` index. // The board specific items, and the rest, have order with `z`. We needed something between `0` and `z` with natural-order. - this.menuModelRegistry.registerSubmenu(boardsSubmenuPath, `Board${!!boardsSubmenuLabel ? `: "${boardsSubmenuLabel}"` : ''}`, { order: '100' }); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => unregisterSubmenu(boardsSubmenuPath, this.menuModelRegistry))); + this.menuModelRegistry.registerSubmenu( + boardsSubmenuPath, + `Board${!!boardsSubmenuLabel ? `: "${boardsSubmenuLabel}"` : ''}`, + { order: '100' } + ); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + unregisterSubmenu(boardsSubmenuPath, this.menuModelRegistry) + ) + ); // Ports submenu - const portsSubmenuPath = [...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, '2_ports']; + const portsSubmenuPath = [ + ...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, + '2_ports', + ]; const portsSubmenuLabel = config.selectedPort?.address; - this.menuModelRegistry.registerSubmenu(portsSubmenuPath, `Port${!!portsSubmenuLabel ? `: "${portsSubmenuLabel}"` : ''}`, { order: '101' }); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => unregisterSubmenu(portsSubmenuPath, this.menuModelRegistry))); + this.menuModelRegistry.registerSubmenu( + portsSubmenuPath, + `Port${!!portsSubmenuLabel ? `: "${portsSubmenuLabel}"` : ''}`, + { order: '101' } + ); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + unregisterSubmenu(portsSubmenuPath, this.menuModelRegistry) + ) + ); - const getBoardInfo = { commandId: BoardSelection.Commands.GET_BOARD_INFO.id, label: 'Get Board Info', order: '103' }; - this.menuModelRegistry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, getBoardInfo); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.menuModelRegistry.unregisterMenuAction(getBoardInfo))); + const getBoardInfo = { + commandId: BoardSelection.Commands.GET_BOARD_INFO.id, + label: 'Get Board Info', + order: '103', + }; + this.menuModelRegistry.registerMenuAction( + ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP, + getBoardInfo + ); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + this.menuModelRegistry.unregisterMenuAction(getBoardInfo) + ) + ); const boardsManagerGroup = [...boardsSubmenuPath, '0_manager']; const boardsPackagesGroup = [...boardsSubmenuPath, '1_packages']; this.menuModelRegistry.registerMenuAction(boardsManagerGroup, { commandId: `${BoardsListWidget.WIDGET_ID}:toggle`, - label: 'Boards Manager...' + label: 'Boards Manager...', }); // Installed boards @@ -122,31 +190,50 @@ PID: ${PID}`; // Platform submenu const platformMenuPath = [...boardsPackagesGroup, packageId]; // Note: Registering the same submenu twice is a noop. No need to group the boards per platform. - this.menuModelRegistry.registerSubmenu(platformMenuPath, packageName); + this.menuModelRegistry.registerSubmenu( + platformMenuPath, + packageName + ); const id = `arduino-select-board--${fqbn}`; const command = { id }; const handler = { execute: () => { - if (fqbn !== this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn) { + if ( + fqbn !== + this.boardsServiceProvider.boardsConfig.selectedBoard + ?.fqbn + ) { this.boardsServiceProvider.boardsConfig = { selectedBoard: { name, fqbn, - port: this.boardsServiceProvider.boardsConfig.selectedBoard?.port // TODO: verify! + port: this.boardsServiceProvider.boardsConfig + .selectedBoard?.port, // TODO: verify! }, - selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort - } + selectedPort: + this.boardsServiceProvider.boardsConfig + .selectedPort, + }; } }, - isToggled: () => fqbn === this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn + isToggled: () => + fqbn === + this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn, }; // Board menu const menuAction = { commandId: id, label: name }; this.commandRegistry.registerCommand(command, handler); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.commandRegistry.unregisterCommand(command))); - this.menuModelRegistry.registerMenuAction(platformMenuPath, menuAction); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + this.commandRegistry.unregisterCommand(command) + ) + ); + this.menuModelRegistry.registerMenuAction( + platformMenuPath, + menuAction + ); // Note: we do not dispose the menu actions individually. Calling `unregisterSubmenu` on the parent will wipe the children menu nodes recursively. } @@ -161,47 +248,77 @@ PID: ${PID}`; const [port] = ports[addresses[0]]; const protocol = port.protocol; const menuPath = [...portsSubmenuPath, protocol]; - const placeholder = new PlaceholderMenuNode(menuPath, `${firstToUpperCase(port.protocol)} ports`); + const placeholder = new PlaceholderMenuNode( + menuPath, + `${firstToUpperCase(port.protocol)} ports` + ); this.menuModelRegistry.registerMenuNode(menuPath, placeholder); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.menuModelRegistry.unregisterMenuNode(placeholder.id))); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + this.menuModelRegistry.unregisterMenuNode(placeholder.id) + ) + ); for (const address of addresses) { if (!!ports[address]) { const [port, boards] = ports[address]; if (!boards.length) { boards.push({ - name: '' + name: '', }); } for (const { name, fqbn } of boards) { - const id = `arduino-select-port--${address}${fqbn ? `--${fqbn}` : ''}`; + const id = `arduino-select-port--${address}${ + fqbn ? `--${fqbn}` : '' + }`; const command = { id }; const handler = { execute: () => { - if (!Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort)) { + if ( + !Port.equals( + port, + this.boardsServiceProvider.boardsConfig + .selectedPort + ) + ) { this.boardsServiceProvider.boardsConfig = { - selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard, - selectedPort: port - } + selectedBoard: + this.boardsServiceProvider + .boardsConfig.selectedBoard, + selectedPort: port, + }; } }, - isToggled: () => Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort) + isToggled: () => + Port.equals( + port, + this.boardsServiceProvider.boardsConfig + .selectedPort + ), }; const label = `${address}${name ? ` (${name})` : ''}`; const menuAction = { commandId: id, label, - order: `1${label}` // `1` comes after the placeholder which has order `0` + order: `1${label}`, // `1` comes after the placeholder which has order `0` }; this.commandRegistry.registerCommand(command, handler); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.commandRegistry.unregisterCommand(command))); - this.menuModelRegistry.registerMenuAction(menuPath, menuAction); + this.toDisposeBeforeMenuRebuild.push( + Disposable.create(() => + this.commandRegistry.unregisterCommand(command) + ) + ); + this.menuModelRegistry.registerMenuAction( + menuPath, + menuAction + ); } } } - } + }; - const { serial, network, unknown } = AvailablePorts.groupByProtocol(availablePorts); + const { serial, network, unknown } = + AvailablePorts.groupByProtocol(availablePorts); registerPorts(serial); registerPorts(network); registerPorts(unknown); @@ -213,7 +330,6 @@ PID: ${PID}`; const allBoards = await this.boardsService.searchBoards({}); return allBoards.filter(InstalledBoardWithPackage.is); } - } export namespace BoardSelection { export namespace Commands { diff --git a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts index 5df464c6..c2d0e485 100644 --- a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts +++ b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts @@ -5,11 +5,15 @@ import { ArduinoMenus } from '../menu/arduino-menus'; import { BoardsDataStore } from '../boards/boards-data-store'; import { MonitorConnection } from '../monitor/monitor-connection'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, +} from './contribution'; @injectable() export class BurnBootloader extends SketchContribution { - @inject(CoreService) protected readonly coreService: CoreService; @@ -27,7 +31,7 @@ export class BurnBootloader extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, { - execute: () => this.burnBootloader() + execute: () => this.burnBootloader(), }); } @@ -35,7 +39,7 @@ export class BurnBootloader extends SketchContribution { registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, { commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id, label: 'Burn Bootloader', - order: 'z99' + order: 'z99', }); } @@ -47,21 +51,28 @@ export class BurnBootloader extends SketchContribution { try { const { boardsConfig } = this.boardsServiceClientImpl; const port = boardsConfig.selectedPort?.address; - const [fqbn, { selectedProgrammer: programmer }, verify, verbose] = await Promise.all([ - this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn), - this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn), - this.preferences.get('arduino.upload.verify'), - this.preferences.get('arduino.upload.verbose') - ]); + const [fqbn, { selectedProgrammer: programmer }, verify, verbose] = + await Promise.all([ + this.boardsDataStore.appendConfigToFqbn( + boardsConfig.selectedBoard?.fqbn + ), + this.boardsDataStore.getData( + boardsConfig.selectedBoard?.fqbn + ), + this.preferences.get('arduino.upload.verify'), + this.preferences.get('arduino.upload.verbose'), + ]); this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.burnBootloader({ fqbn, programmer, port, verify, - verbose + verbose, + }); + this.messageService.info('Done burning bootloader.', { + timeout: 3000, }); - this.messageService.info('Done burning bootloader.', { timeout: 3000 }); } catch (e) { this.messageService.error(e.toString()); } finally { @@ -70,13 +81,12 @@ export class BurnBootloader extends SketchContribution { } } } - } export namespace BurnBootloader { export namespace Commands { export const BURN_BOOTLOADER: Command = { - id: 'arduino-burn-bootloader' + id: 'arduino-burn-bootloader', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/close.ts b/arduino-ide-extension/src/browser/contributions/close.ts index 46bd72ce..a096b557 100644 --- a/arduino-ide-extension/src/browser/contributions/close.ts +++ b/arduino-ide-extension/src/browser/contributions/close.ts @@ -7,14 +7,20 @@ import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shel import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { ArduinoMenus } from '../menu/arduino-menus'; import { SaveAsSketch } from './save-as-sketch'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, URI } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + URI, +} from './contribution'; /** * Closes the `current` closeable editor, or any closeable current widget from the main area, or the current sketch window. */ @injectable() export class Close extends SketchContribution { - @inject(EditorManager) protected readonly editorManager: EditorManager; @@ -27,7 +33,6 @@ export class Close extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(Close.Commands.CLOSE, { execute: async () => { - // Close current editor if closeable. const { currentEditor } = this.editorManager; if (currentEditor && currentEditor.title.closable) { @@ -38,8 +43,13 @@ export class Close extends SketchContribution { // Close current widget from the main area if possible. const { currentWidget } = this.shell; if (currentWidget) { - const currentWidgetInMain = toArray(this.shell.mainPanel.widgets()).find(widget => widget === currentWidget); - if (currentWidgetInMain && currentWidgetInMain.title.closable) { + const currentWidgetInMain = toArray( + this.shell.mainPanel.widgets() + ).find((widget) => widget === currentWidget); + if ( + currentWidgetInMain && + currentWidgetInMain.title.closable + ) { return currentWidgetInMain.close(); } } @@ -54,25 +64,32 @@ export class Close extends SketchContribution { if (!uri) { return; } - if (isTemp && await this.wasTouched(uri)) { + if (isTemp && (await this.wasTouched(uri))) { const { response } = await remote.dialog.showMessageBox({ type: 'question', buttons: ["Don't Save", 'Cancel', 'Save'], - message: 'Do you want to save changes to this sketch before closing?', - detail: "If you don't save, your changes will be lost." + message: + 'Do you want to save changes to this sketch before closing?', + detail: "If you don't save, your changes will be lost.", }); - if (response === 1) { // Cancel + if (response === 1) { + // Cancel return; } - if (response === 2) { // Save - const saved = await this.commandService.executeCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH.id, { openAfterMove: false, execOnlyIfTemp: true }); - if (!saved) { // If it was not saved, do bail the close. + if (response === 2) { + // Save + const saved = await this.commandService.executeCommand( + SaveAsSketch.Commands.SAVE_AS_SKETCH.id, + { openAfterMove: false, execOnlyIfTemp: true } + ); + if (!saved) { + // If it was not saved, do bail the close. return; } } } window.close(); - } + }, }); } @@ -80,14 +97,14 @@ export class Close extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: Close.Commands.CLOSE.id, label: 'Close', - order: '5' + order: '5', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: Close.Commands.CLOSE.id, - keybinding: 'CtrlCmd+W' + keybinding: 'CtrlCmd+W', }); } @@ -99,7 +116,10 @@ export class Close extends SketchContribution { if (editorWidget) { const { editor } = editorWidget; if (editor instanceof MonacoEditor) { - const versionId = editor.getControl().getModel()?.getVersionId(); + const versionId = editor + .getControl() + .getModel() + ?.getVersionId(); if (Number.isInteger(versionId) && versionId! > 1) { return true; } @@ -107,13 +127,12 @@ export class Close extends SketchContribution { } return false; } - } export namespace Close { export namespace Commands { export const CLOSE: Command = { - id: 'arduino-close' + id: 'arduino-close', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/contribution.ts b/arduino-ide-extension/src/browser/contributions/contribution.ts index af4a5d47..a8c0c400 100644 --- a/arduino-ide-extension/src/browser/contributions/contribution.ts +++ b/arduino-ide-extension/src/browser/contributions/contribution.ts @@ -10,22 +10,59 @@ import { MessageService } from '@theia/core/lib/common/message-service'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { open, OpenerService } from '@theia/core/lib/browser/opener-service'; import { OutputChannelManager } from '@theia/output/lib/common/output-channel'; -import { MenuModelRegistry, MenuContribution } from '@theia/core/lib/common/menu'; -import { KeybindingRegistry, KeybindingContribution } from '@theia/core/lib/browser/keybinding'; -import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser/frontend-application'; -import { Command, CommandRegistry, CommandContribution, CommandService } from '@theia/core/lib/common/command'; +import { + MenuModelRegistry, + MenuContribution, +} from '@theia/core/lib/common/menu'; +import { + KeybindingRegistry, + KeybindingContribution, +} from '@theia/core/lib/browser/keybinding'; +import { + TabBarToolbarContribution, + TabBarToolbarRegistry, +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { + FrontendApplicationContribution, + FrontendApplication, +} from '@theia/core/lib/browser/frontend-application'; +import { + Command, + CommandRegistry, + CommandContribution, + CommandService, +} from '@theia/core/lib/common/command'; import { EditorMode } from '../editor-mode'; import { SettingsService } from '../settings'; import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl'; -import { SketchesService, ConfigService, FileSystemExt, Sketch } from '../../common/protocol'; +import { + SketchesService, + ConfigService, + FileSystemExt, + Sketch, +} from '../../common/protocol'; import { ArduinoPreferences } from '../arduino-preferences'; -export { Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry, URI, Sketch, open }; +export { + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, + URI, + Sketch, + open, +}; @injectable() -export abstract class Contribution implements CommandContribution, MenuContribution, KeybindingContribution, TabBarToolbarContribution, FrontendApplicationContribution { - +export abstract class Contribution + implements + CommandContribution, + MenuContribution, + KeybindingContribution, + TabBarToolbarContribution, + FrontendApplicationContribution +{ @inject(ILogger) protected readonly logger: ILogger; @@ -47,26 +84,19 @@ export abstract class Contribution implements CommandContribution, MenuContribut @inject(SettingsService) protected readonly settingsService: SettingsService; - onStart(app: FrontendApplication): MaybePromise { - } + onStart(app: FrontendApplication): MaybePromise {} - registerCommands(registry: CommandRegistry): void { - } + registerCommands(registry: CommandRegistry): void {} - registerMenus(registry: MenuModelRegistry): void { - } + registerMenus(registry: MenuModelRegistry): void {} - registerKeybindings(registry: KeybindingRegistry): void { - } - - registerToolbarItems(registry: TabBarToolbarRegistry): void { - } + registerKeybindings(registry: KeybindingRegistry): void {} + registerToolbarItems(registry: TabBarToolbarRegistry): void {} } @injectable() export abstract class SketchContribution extends Contribution { - @inject(FileService) protected readonly fileService: FileService; @@ -100,18 +130,23 @@ export abstract class SketchContribution extends Contribution { if (sketch) { for (const editor of this.editorManager.all) { const uri = editor.editor.uri; - if (Saveable.isDirty(editor) && Sketch.isInSketch(uri, sketch)) { + if ( + Saveable.isDirty(editor) && + Sketch.isInSketch(uri, sketch) + ) { override[uri.toString()] = editor.editor.document.getText(); } } } return override; } - } export namespace Contribution { - export function configure(bind: interfaces.Bind, serviceIdentifier: typeof Contribution): void { + export function configure( + bind: interfaces.Bind, + serviceIdentifier: typeof Contribution + ): void { bind(serviceIdentifier).toSelf().inSingletonScope(); bind(CommandContribution).toService(serviceIdentifier); bind(MenuContribution).toService(serviceIdentifier); diff --git a/arduino-ide-extension/src/browser/contributions/debug.ts b/arduino-ide-extension/src/browser/contributions/debug.ts index 193922c4..ff6f5825 100644 --- a/arduino-ide-extension/src/browser/contributions/debug.ts +++ b/arduino-ide-extension/src/browser/contributions/debug.ts @@ -5,11 +5,16 @@ import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { NotificationCenter } from '../notification-center'; import { Board, BoardsService, ExecutableService } from '../../common/protocol'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { URI, Command, CommandRegistry, SketchContribution, TabBarToolbarRegistry } from './contribution'; +import { + URI, + Command, + CommandRegistry, + SketchContribution, + TabBarToolbarRegistry, +} from './contribution'; @injectable() export class Debug extends SketchContribution { - @inject(HostedPluginSupport) protected hostedPluginSupport: HostedPluginSupport; @@ -29,8 +34,11 @@ export class Debug extends SketchContribution { * If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled. */ protected _disabledMessages?: string = 'No board selected'; // Initial pessimism. - protected disabledMessageDidChangeEmitter = new Emitter(); - protected onDisabledMessageDidChange = this.disabledMessageDidChangeEmitter.event; + protected disabledMessageDidChangeEmitter = new Emitter< + string | undefined + >(); + protected onDisabledMessageDidChange = + this.disabledMessageDidChangeEmitter.event; protected get disabledMessage(): string | undefined { return this._disabledMessages; @@ -43,14 +51,28 @@ export class Debug extends SketchContribution { protected readonly debugToolbarItem = { id: Debug.Commands.START_DEBUGGING.id, command: Debug.Commands.START_DEBUGGING.id, - tooltip: `${this.disabledMessage ? `Debug - ${this.disabledMessage}` : 'Start Debugging'}`, + tooltip: `${ + this.disabledMessage + ? `Debug - ${this.disabledMessage}` + : 'Start Debugging' + }`, priority: 3, - onDidChange: this.onDisabledMessageDidChange as Event + onDidChange: this.onDisabledMessageDidChange as Event, }; onStart(): void { - this.onDisabledMessageDidChange(() => this.debugToolbarItem.tooltip = `${this.disabledMessage ? `Debug - ${this.disabledMessage}` : 'Start Debugging'}`); - const refreshState = async (board: Board | undefined = this.boardsServiceProvider.boardsConfig.selectedBoard) => { + this.onDisabledMessageDidChange( + () => + (this.debugToolbarItem.tooltip = `${ + this.disabledMessage + ? `Debug - ${this.disabledMessage}` + : 'Start Debugging' + }`) + ); + const refreshState = async ( + board: Board | undefined = this.boardsServiceProvider.boardsConfig + .selectedBoard + ) => { if (!board) { this.disabledMessage = 'No board selected'; return; @@ -71,8 +93,10 @@ export class Debug extends SketchContribution { } else { this.disabledMessage = undefined; } - } - this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) => refreshState(selectedBoard)); + }; + this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) => + refreshState(selectedBoard) + ); this.notificationCenter.onPlatformInstalled(() => refreshState()); this.notificationCenter.onPlatformUninstalled(() => refreshState()); refreshState(); @@ -81,8 +105,9 @@ export class Debug extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(Debug.Commands.START_DEBUGGING, { execute: () => this.startDebug(), - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - isEnabled: () => !this.disabledMessage + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', + isEnabled: () => !this.disabledMessage, }); } @@ -90,7 +115,10 @@ export class Debug extends SketchContribution { registry.registerItem(this.debugToolbarItem); } - protected async startDebug(board: Board | undefined = this.boardsServiceProvider.boardsConfig.selectedBoard): Promise { + protected async startDebug( + board: Board | undefined = this.boardsServiceProvider.boardsConfig + .selectedBoard + ): Promise { if (!board) { return; } @@ -101,29 +129,33 @@ export class Debug extends SketchContribution { await this.hostedPluginSupport.didStart; const [sketch, executables] = await Promise.all([ this.sketchServiceClient.currentSketch(), - this.executableService.list() + this.executableService.list(), ]); if (!sketch) { return; } - const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri(sketch); + const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri( + sketch + ); const [cliPath, sketchPath, configPath] = await Promise.all([ this.fileService.fsPath(new URI(executables.cliUri)), this.fileService.fsPath(new URI(sketch.uri)), this.fileService.fsPath(new URI(ideTempFolderUri)), - ]) + ]); const config = { cliPath, board: { fqbn, - name + name, }, sketchPath, - configPath + configPath, }; - return this.commandService.executeCommand('arduino.debug.start', config); + return this.commandService.executeCommand( + 'arduino.debug.start', + config + ); } - } export namespace Debug { @@ -131,7 +163,7 @@ export namespace Debug { export const START_DEBUGGING: Command = { id: 'arduino-start-debug', label: 'Start Debugging', - category: 'Arduino' - } + category: 'Arduino', + }; } } diff --git a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts index d80be3da..f4886fb9 100644 --- a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts +++ b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts @@ -3,14 +3,19 @@ import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribu import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; -import { Contribution, Command, MenuModelRegistry, KeybindingRegistry, CommandRegistry } from './contribution'; +import { + Contribution, + Command, + MenuModelRegistry, + KeybindingRegistry, + CommandRegistry, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; // TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072 // Depends on https://github.com/eclipse-theia/theia/pull/7964 @injectable() export class EditContributions extends Contribution { - @inject(MonacoEditorService) protected readonly codeEditorService: MonacoEditorService; @@ -21,48 +26,74 @@ export class EditContributions extends Contribution { protected readonly preferences: PreferenceService; registerCommands(registry: CommandRegistry): void { - registry.registerCommand(EditContributions.Commands.GO_TO_LINE, { execute: () => this.run('editor.action.gotoLine') }); - registry.registerCommand(EditContributions.Commands.TOGGLE_COMMENT, { execute: () => this.run('editor.action.commentLine') }); - registry.registerCommand(EditContributions.Commands.INDENT_LINES, { execute: () => this.run('editor.action.indentLines') }); - registry.registerCommand(EditContributions.Commands.OUTDENT_LINES, { execute: () => this.run('editor.action.outdentLines') }); - registry.registerCommand(EditContributions.Commands.FIND, { execute: () => this.run('actions.find') }); - registry.registerCommand(EditContributions.Commands.FIND_NEXT, { execute: () => this.run('actions.findWithSelection') }); - registry.registerCommand(EditContributions.Commands.FIND_PREVIOUS, { execute: () => this.run('editor.action.nextMatchFindAction') }); - registry.registerCommand(EditContributions.Commands.USE_FOR_FIND, { execute: () => this.run('editor.action.previousSelectionMatchFindAction') }); - registry.registerCommand(EditContributions.Commands.INCREASE_FONT_SIZE, { - execute: async () => { - const settings = await this.settingsService.settings(); - if (settings.autoScaleInterface) { - settings.interfaceScale = settings.interfaceScale + 1; - } else { - settings.editorFontSize = settings.editorFontSize + 1; - } - await this.settingsService.update(settings); - await this.settingsService.save(); - } + registry.registerCommand(EditContributions.Commands.GO_TO_LINE, { + execute: () => this.run('editor.action.gotoLine'), }); - registry.registerCommand(EditContributions.Commands.DECREASE_FONT_SIZE, { - execute: async () => { - const settings = await this.settingsService.settings(); - if (settings.autoScaleInterface) { - settings.interfaceScale = settings.interfaceScale - 1; - } else { - settings.editorFontSize = settings.editorFontSize - 1; - } - await this.settingsService.update(settings); - await this.settingsService.save(); - } + registry.registerCommand(EditContributions.Commands.TOGGLE_COMMENT, { + execute: () => this.run('editor.action.commentLine'), }); - /* Tools */registry.registerCommand(EditContributions.Commands.AUTO_FORMAT, { execute: () => this.run('editor.action.formatDocument') }); + registry.registerCommand(EditContributions.Commands.INDENT_LINES, { + execute: () => this.run('editor.action.indentLines'), + }); + registry.registerCommand(EditContributions.Commands.OUTDENT_LINES, { + execute: () => this.run('editor.action.outdentLines'), + }); + registry.registerCommand(EditContributions.Commands.FIND, { + execute: () => this.run('actions.find'), + }); + registry.registerCommand(EditContributions.Commands.FIND_NEXT, { + execute: () => this.run('actions.findWithSelection'), + }); + registry.registerCommand(EditContributions.Commands.FIND_PREVIOUS, { + execute: () => this.run('editor.action.nextMatchFindAction'), + }); + registry.registerCommand(EditContributions.Commands.USE_FOR_FIND, { + execute: () => + this.run('editor.action.previousSelectionMatchFindAction'), + }); + registry.registerCommand( + EditContributions.Commands.INCREASE_FONT_SIZE, + { + execute: async () => { + const settings = await this.settingsService.settings(); + if (settings.autoScaleInterface) { + settings.interfaceScale = settings.interfaceScale + 1; + } else { + settings.editorFontSize = settings.editorFontSize + 1; + } + await this.settingsService.update(settings); + await this.settingsService.save(); + }, + } + ); + registry.registerCommand( + EditContributions.Commands.DECREASE_FONT_SIZE, + { + execute: async () => { + const settings = await this.settingsService.settings(); + if (settings.autoScaleInterface) { + settings.interfaceScale = settings.interfaceScale - 1; + } else { + settings.editorFontSize = settings.editorFontSize - 1; + } + await this.settingsService.update(settings); + await this.settingsService.save(); + }, + } + ); + /* Tools */ registry.registerCommand( + EditContributions.Commands.AUTO_FORMAT, + { execute: () => this.run('editor.action.formatDocument') } + ); registry.registerCommand(EditContributions.Commands.COPY_FOR_FORUM, { execute: async () => { const value = await this.currentValue(); if (value !== undefined) { this.clipboardService.writeText(`[code] ${value} -[/code]`) +[/code]`); } - } + }, }); registry.registerCommand(EditContributions.Commands.COPY_FOR_GITHUB, { execute: async () => { @@ -70,98 +101,98 @@ ${value} if (value !== undefined) { this.clipboardService.writeText(`\`\`\`cpp ${value} -\`\`\``) +\`\`\``); } - } + }, }); } registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: CommonCommands.CUT.id, - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: CommonCommands.COPY.id, - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: EditContributions.Commands.COPY_FOR_FORUM.id, label: 'Copy for Forum', - order: '2' + order: '2', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: EditContributions.Commands.COPY_FOR_GITHUB.id, label: 'Copy for GitHub', - order: '3' + order: '3', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: CommonCommands.PASTE.id, - order: '4' + order: '4', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: CommonCommands.SELECT_ALL.id, - order: '5' + order: '5', }); registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: EditContributions.Commands.GO_TO_LINE.id, label: 'Go to Line...', - order: '6' + order: '6', }); registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, { commandId: EditContributions.Commands.TOGGLE_COMMENT.id, label: 'Comment/Uncomment', - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, { commandId: EditContributions.Commands.INDENT_LINES.id, label: 'Increase Indent', - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, { commandId: EditContributions.Commands.OUTDENT_LINES.id, label: 'Decrease Indent', - order: '2' + order: '2', }); registry.registerMenuAction(ArduinoMenus.EDIT__FONT_CONTROL_GROUP, { commandId: EditContributions.Commands.INCREASE_FONT_SIZE.id, label: 'Increase Font Size', - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.EDIT__FONT_CONTROL_GROUP, { commandId: EditContributions.Commands.DECREASE_FONT_SIZE.id, label: 'Decrease Font Size', - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, { commandId: EditContributions.Commands.FIND.id, label: 'Find', - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, { commandId: EditContributions.Commands.FIND_NEXT.id, label: 'Find Next', - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, { commandId: EditContributions.Commands.FIND_PREVIOUS.id, label: 'Find Previous', - order: '2' + order: '2', }); registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, { commandId: EditContributions.Commands.USE_FOR_FIND.id, label: 'Use Selection for Find', // XXX: The Java IDE uses `Use Selection For Find`. - order: '3' + order: '3', }); // `Tools` registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { commandId: EditContributions.Commands.AUTO_FORMAT.id, label: 'Auto Format', // XXX: The Java IDE uses `Use Selection For Find`. - order: '0' + order: '0', }); } @@ -169,60 +200,63 @@ ${value} registry.registerKeybinding({ command: EditContributions.Commands.COPY_FOR_FORUM.id, keybinding: 'CtrlCmd+Shift+C', - when: 'editorFocus' + when: 'editorFocus', }); registry.registerKeybinding({ command: EditContributions.Commands.COPY_FOR_GITHUB.id, keybinding: 'CtrlCmd+Alt+C', - when: 'editorFocus' + when: 'editorFocus', }); registry.registerKeybinding({ command: EditContributions.Commands.GO_TO_LINE.id, keybinding: 'CtrlCmd+L', - when: 'editorFocus' + when: 'editorFocus', }); registry.registerKeybinding({ command: EditContributions.Commands.TOGGLE_COMMENT.id, keybinding: 'CtrlCmd+/', - when: 'editorFocus' + when: 'editorFocus', }); registry.registerKeybinding({ command: EditContributions.Commands.INCREASE_FONT_SIZE.id, - keybinding: 'CtrlCmd+=' + keybinding: 'CtrlCmd+=', }); registry.registerKeybinding({ command: EditContributions.Commands.DECREASE_FONT_SIZE.id, - keybinding: 'CtrlCmd+-' + keybinding: 'CtrlCmd+-', }); registry.registerKeybinding({ command: EditContributions.Commands.FIND.id, - keybinding: 'CtrlCmd+F' + keybinding: 'CtrlCmd+F', }); registry.registerKeybinding({ command: EditContributions.Commands.FIND_NEXT.id, - keybinding: 'CtrlCmd+G' + keybinding: 'CtrlCmd+G', }); registry.registerKeybinding({ command: EditContributions.Commands.FIND_PREVIOUS.id, - keybinding: 'CtrlCmd+Shift+G' + keybinding: 'CtrlCmd+Shift+G', }); registry.registerKeybinding({ command: EditContributions.Commands.USE_FOR_FIND.id, - keybinding: 'CtrlCmd+E' + keybinding: 'CtrlCmd+E', }); // `Tools` registry.registerKeybinding({ command: EditContributions.Commands.AUTO_FORMAT.id, - keybinding: 'CtrlCmd+T' + keybinding: 'CtrlCmd+T', }); } protected async current(): Promise { - return this.codeEditorService.getFocusedCodeEditor() || this.codeEditorService.getActiveCodeEditor(); + return ( + this.codeEditorService.getFocusedCodeEditor() || + this.codeEditorService.getActiveCodeEditor() + ); } protected async currentValue(): Promise { @@ -246,49 +280,48 @@ ${value} } } } - } export namespace EditContributions { export namespace Commands { export const COPY_FOR_FORUM: Command = { - id: 'arduino-copy-for-forum' + id: 'arduino-copy-for-forum', }; export const COPY_FOR_GITHUB: Command = { - id: 'arduino-copy-for-github' + id: 'arduino-copy-for-github', }; export const GO_TO_LINE: Command = { - id: 'arduino-go-to-line' + id: 'arduino-go-to-line', }; export const TOGGLE_COMMENT: Command = { - id: 'arduino-toggle-comment' + id: 'arduino-toggle-comment', }; export const INDENT_LINES: Command = { - id: 'arduino-indent-lines' + id: 'arduino-indent-lines', }; export const OUTDENT_LINES: Command = { - id: 'arduino-outdent-lines' + id: 'arduino-outdent-lines', }; export const FIND: Command = { - id: 'arduino-find' + id: 'arduino-find', }; export const FIND_NEXT: Command = { - id: 'arduino-find-next' + id: 'arduino-find-next', }; export const FIND_PREVIOUS: Command = { - id: 'arduino-find-previous' + id: 'arduino-find-previous', }; export const USE_FOR_FIND: Command = { - id: 'arduino-for-find' + id: 'arduino-for-find', }; export const INCREASE_FONT_SIZE: Command = { - id: 'arduino-increase-font-size' + id: 'arduino-increase-font-size', }; export const DECREASE_FONT_SIZE: Command = { - id: 'arduino-decrease-font-size' + id: 'arduino-decrease-font-size', }; export const AUTO_FORMAT: Command = { - id: 'arduino-auto-format' // `Auto Format` should belong to `Tool`. + id: 'arduino-auto-format', // `Auto Format` should belong to `Tool`. }; } } diff --git a/arduino-ide-extension/src/browser/contributions/examples.ts b/arduino-ide-extension/src/browser/contributions/examples.ts index 510b3c4e..37a8dd4f 100644 --- a/arduino-ide-extension/src/browser/contributions/examples.ts +++ b/arduino-ide-extension/src/browser/contributions/examples.ts @@ -1,20 +1,30 @@ import * as PQueue from 'p-queue'; import { inject, injectable, postConstruct } from 'inversify'; import { CommandHandler } from '@theia/core/lib/common/command'; -import { MenuPath, CompositeMenuNode, SubMenuOptions } from '@theia/core/lib/common/menu'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; +import { + MenuPath, + CompositeMenuNode, + SubMenuOptions, +} from '@theia/core/lib/common/menu'; +import { + Disposable, + DisposableCollection, +} from '@theia/core/lib/common/disposable'; import { OpenSketch } from './open-sketch'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { MainMenuManager } from '../../common/main-menu-manager'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { ExamplesService } from '../../common/protocol/examples-service'; -import { SketchContribution, CommandRegistry, MenuModelRegistry } from './contribution'; +import { + SketchContribution, + CommandRegistry, + MenuModelRegistry, +} from './contribution'; import { NotificationCenter } from '../notification-center'; import { Board, Sketch, SketchContainer } from '../../common/protocol'; @injectable() export abstract class Examples extends SketchContribution { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -34,7 +44,9 @@ export abstract class Examples extends SketchContribution { @postConstruct() init(): void { - this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.handleBoardChanged(selectedBoard)); + this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => + this.handleBoardChanged(selectedBoard) + ); } protected handleBoardChanged(board: Board | undefined): void { @@ -46,8 +58,13 @@ export abstract class Examples extends SketchContribution { // 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 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) { @@ -56,19 +73,33 @@ export abstract class Examples extends SketchContribution { } // 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' }); + registry.registerSubmenu( + ArduinoMenus.FILE__EXAMPLES_SUBMENU, + 'Examples', + { order: '4' } + ); } registerRecursively( - sketchContainerOrPlaceholder: SketchContainer | (Sketch | SketchContainer)[] | string, + sketchContainerOrPlaceholder: + | SketchContainer + | (Sketch | SketchContainer)[] + | string, menuPath: MenuPath, pushToDispose: DisposableCollection = new DisposableCollection(), - subMenuOptions?: SubMenuOptions | undefined): void { - + subMenuOptions?: SubMenuOptions | undefined + ): void { if (typeof sketchContainerOrPlaceholder === 'string') { - const placeholder = new PlaceholderMenuNode(menuPath, sketchContainerOrPlaceholder); + const placeholder = new PlaceholderMenuNode( + menuPath, + sketchContainerOrPlaceholder + ); this.menuRegistry.registerMenuNode(menuPath, placeholder); - pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuNode(placeholder.id))); + pushToDispose.push( + Disposable.create(() => + this.menuRegistry.unregisterMenuNode(placeholder.id) + ) + ); } else { const sketches: Sketch[] = []; const children: SketchContainer[] = []; @@ -77,7 +108,11 @@ export abstract class Examples extends SketchContribution { if (SketchContainer.is(sketchContainerOrPlaceholder)) { const { label } = sketchContainerOrPlaceholder; submenuPath = [...menuPath, label]; - this.menuRegistry.registerSubmenu(submenuPath, label, subMenuOptions); + this.menuRegistry.registerSubmenu( + submenuPath, + label, + subMenuOptions + ); sketches.push(...sketchContainerOrPlaceholder.sketches); children.push(...sketchContainerOrPlaceholder.children); } else { @@ -89,15 +124,29 @@ export abstract class Examples extends SketchContribution { } } } - children.forEach(child => this.registerRecursively(child, submenuPath, pushToDispose)); + 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 commandId = `arduino-open-example-${submenuPath.join( + ':' + )}--${uri}`; const command = { id: commandId }; const handler = this.createHandler(uri); - pushToDispose.push(this.commandRegistry.registerCommand(command, handler)); - this.menuRegistry.registerMenuAction(submenuPath, { commandId, label: sketch.name, order: sketch.name.toLocaleLowerCase() }); - pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(command))); + pushToDispose.push( + this.commandRegistry.registerCommand(command, handler) + ); + this.menuRegistry.registerMenuAction(submenuPath, { + commandId, + label: sketch.name, + order: sketch.name.toLocaleLowerCase(), + }); + pushToDispose.push( + Disposable.create(() => + this.menuRegistry.unregisterMenuAction(command) + ) + ); } } } @@ -106,16 +155,17 @@ export abstract class Examples extends SketchContribution { return { execute: async () => { const sketch = await this.sketchService.cloneExample(uri); - return this.commandService.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch); - } - } + return this.commandService.executeCommand( + OpenSketch.Commands.OPEN_SKETCH.id, + sketch + ); + }, + }; } - } @injectable() export class BuiltInExamples extends Examples { - onStart(): void { this.register(); // no `await` } @@ -126,21 +176,25 @@ export class BuiltInExamples extends Examples { sketchContainers = await this.examplesService.builtIns(); } catch (e) { console.error('Could not initialize built-in examples.', e); - this.messageService.error('Could not initialize built-in examples.'); + this.messageService.error( + 'Could not initialize built-in examples.' + ); return; } this.toDispose.dispose(); for (const container of ['Built-in examples', ...sketchContainers]) { - this.registerRecursively(container, ArduinoMenus.EXAMPLES__BUILT_IN_GROUP, this.toDispose); + 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; @@ -156,13 +210,18 @@ export class LibraryExamples extends Examples { this.register(board); } - protected async register(board: Board | undefined = this.boardsServiceClient.boardsConfig.selectedBoard): Promise { + protected async register( + board: Board | undefined = this.boardsServiceClient.boardsConfig + .selectedBoard + ): Promise { return this.queue.add(async () => { this.toDispose.dispose(); const fqbn = board?.fqbn; const name = board?.name; // Shows all examples when no board is selected, or the platform of the currently selected board is not installed. - const { user, current, any } = await this.examplesService.installed({ fqbn }); + const { user, current, any } = await this.examplesService.installed( + { fqbn } + ); if (user.length) { (user as any).unshift('Examples from Custom Libraries'); } @@ -173,16 +232,27 @@ export class LibraryExamples extends Examples { (any as any).unshift('Examples for any board'); } for (const container of user) { - this.registerRecursively(container, ArduinoMenus.EXAMPLES__USER_LIBS_GROUP, this.toDispose); + 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); + 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.registerRecursively( + container, + ArduinoMenus.EXAMPLES__ANY_BOARD_GROUP, + this.toDispose + ); } this.menuManager.update(); }); } - } diff --git a/arduino-ide-extension/src/browser/contributions/help.ts b/arduino-ide-extension/src/browser/contributions/help.ts index 55348637..fee91c9b 100644 --- a/arduino-ide-extension/src/browser/contributions/help.ts +++ b/arduino-ide-extension/src/browser/contributions/help.ts @@ -5,11 +5,16 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { CommandHandler } from '@theia/core/lib/common/command'; import { QuickInputService } from '@theia/core/lib/browser/quick-open/quick-input-service'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { Contribution, Command, MenuModelRegistry, CommandRegistry, KeybindingRegistry } from './contribution'; +import { + Contribution, + Command, + MenuModelRegistry, + CommandRegistry, + KeybindingRegistry, +} from './contribution'; @injectable() export class Help extends Contribution { - @inject(EditorManager) protected readonly editorManager: EditorManager; @@ -20,80 +25,110 @@ export class Help extends Contribution { protected readonly quickInputService: QuickInputService; registerCommands(registry: CommandRegistry): void { - const open = (url: string) => this.windowService.openNewWindow(url, { external: true }); - const createOpenHandler = (url: string) => { - execute: () => open(url) - }; - registry.registerCommand(Help.Commands.GETTING_STARTED, createOpenHandler('https://www.arduino.cc/en/Guide')); - registry.registerCommand(Help.Commands.ENVIRONMENT, createOpenHandler('https://www.arduino.cc/en/Guide/Environment')); - registry.registerCommand(Help.Commands.TROUBLESHOOTING, createOpenHandler('https://support.arduino.cc/hc/en-us')); - registry.registerCommand(Help.Commands.REFERENCE, createOpenHandler('https://www.arduino.cc/reference/en/')); + const open = (url: string) => + this.windowService.openNewWindow(url, { external: true }); + const createOpenHandler = (url: string) => + { + execute: () => open(url), + }; + registry.registerCommand( + Help.Commands.GETTING_STARTED, + createOpenHandler('https://www.arduino.cc/en/Guide') + ); + registry.registerCommand( + Help.Commands.ENVIRONMENT, + createOpenHandler('https://www.arduino.cc/en/Guide/Environment') + ); + registry.registerCommand( + Help.Commands.TROUBLESHOOTING, + createOpenHandler('https://support.arduino.cc/hc/en-us') + ); + registry.registerCommand( + Help.Commands.REFERENCE, + createOpenHandler('https://www.arduino.cc/reference/en/') + ); registry.registerCommand(Help.Commands.FIND_IN_REFERENCE, { execute: async () => { let searchFor: string | undefined = undefined; const { currentEditor } = this.editorManager; - if (currentEditor && currentEditor.editor instanceof MonacoEditor) { + if ( + currentEditor && + currentEditor.editor instanceof MonacoEditor + ) { const codeEditor = currentEditor.editor.getControl(); const selection = codeEditor.getSelection(); const model = codeEditor.getModel(); - if (model && selection && !monaco.Range.isEmpty(selection)) { + if ( + model && + selection && + !monaco.Range.isEmpty(selection) + ) { searchFor = model.getValueInRange(selection); } } if (!searchFor) { searchFor = await this.quickInputService.open({ prompt: 'Search on Arduino.cc', - placeHolder: 'Type a keyword' + placeHolder: 'Type a keyword', }); } if (searchFor) { - return open(`https://www.arduino.cc/search?q=${encodeURIComponent(searchFor)}&tab=reference`); + return open( + `https://www.arduino.cc/search?q=${encodeURIComponent( + searchFor + )}&tab=reference` + ); } - } + }, }); - registry.registerCommand(Help.Commands.FAQ, createOpenHandler('https://support.arduino.cc/hc/en-us')); - registry.registerCommand(Help.Commands.VISIT_ARDUINO, createOpenHandler('https://www.arduino.cc/')); + registry.registerCommand( + Help.Commands.FAQ, + createOpenHandler('https://support.arduino.cc/hc/en-us') + ); + registry.registerCommand( + Help.Commands.VISIT_ARDUINO, + createOpenHandler('https://www.arduino.cc/') + ); } registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, { commandId: Help.Commands.GETTING_STARTED.id, - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, { commandId: Help.Commands.ENVIRONMENT.id, - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, { commandId: Help.Commands.TROUBLESHOOTING.id, - order: '2' + order: '2', }); registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, { commandId: Help.Commands.REFERENCE.id, - order: '3' + order: '3', }); registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, { commandId: Help.Commands.FIND_IN_REFERENCE.id, - order: '4' + order: '4', }); registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, { commandId: Help.Commands.FAQ.id, - order: '5' + order: '5', }); registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, { commandId: Help.Commands.VISIT_ARDUINO.id, - order: '6' + order: '6', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: Help.Commands.FIND_IN_REFERENCE.id, - keybinding: 'CtrlCmd+Shift+F' + keybinding: 'CtrlCmd+Shift+F', }); } - } export namespace Help { @@ -101,37 +136,37 @@ export namespace Help { export const GETTING_STARTED: Command = { id: 'arduino-getting-started', label: 'Getting Started', - category: 'Arduino' + category: 'Arduino', }; export const ENVIRONMENT: Command = { id: 'arduino-environment', label: 'Environment', - category: 'Arduino' + category: 'Arduino', }; export const TROUBLESHOOTING: Command = { id: 'arduino-troubleshooting', label: 'Troubleshooting', - category: 'Arduino' + category: 'Arduino', }; export const REFERENCE: Command = { id: 'arduino-reference', label: 'Reference', - category: 'Arduino' + category: 'Arduino', }; export const FIND_IN_REFERENCE: Command = { id: 'arduino-find-in-reference', label: 'Find in Reference', - category: 'Arduino' + category: 'Arduino', }; export const FAQ: Command = { id: 'arduino-faq', label: 'Frequently Asked Questions', - category: 'Arduino' + category: 'Arduino', }; export const VISIT_ARDUINO: Command = { id: 'arduino-visit-arduino', label: 'Visit Arduino.cc', - category: 'Arduino' + category: 'Arduino', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/include-library.ts b/arduino-ide-extension/src/browser/contributions/include-library.ts index 01264594..b83b060c 100644 --- a/arduino-ide-extension/src/browser/contributions/include-library.ts +++ b/arduino-ide-extension/src/browser/contributions/include-library.ts @@ -4,7 +4,10 @@ import URI from '@theia/core/lib/common/uri'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { EditorManager } from '@theia/editor/lib/browser'; import { MenuModelRegistry, MenuPath } from '@theia/core/lib/common/menu'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; +import { + Disposable, + DisposableCollection, +} from '@theia/core/lib/common/disposable'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { LibraryPackage, LibraryService } from '../../common/protocol'; import { MainMenuManager } from '../../common/main-menu-manager'; @@ -15,7 +18,6 @@ import { NotificationCenter } from '../notification-center'; @injectable() export class IncludeLibrary extends SketchContribution { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -42,29 +44,40 @@ export class IncludeLibrary extends SketchContribution { onStart(): void { this.updateMenuActions(); - this.boardsServiceClient.onBoardsConfigChanged(() => this.updateMenuActions()) - this.notificationCenter.onLibraryInstalled(() => this.updateMenuActions()); - this.notificationCenter.onLibraryUninstalled(() => this.updateMenuActions()); + this.boardsServiceClient.onBoardsConfigChanged(() => + this.updateMenuActions() + ); + this.notificationCenter.onLibraryInstalled(() => + this.updateMenuActions() + ); + this.notificationCenter.onLibraryUninstalled(() => + this.updateMenuActions() + ); } registerMenus(registry: MenuModelRegistry): void { // `Include Library` submenu - const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include']; - registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' }); + const includeLibMenuPath = [ + ...ArduinoMenus.SKETCH__UTILS_GROUP, + '0_include', + ]; + registry.registerSubmenu(includeLibMenuPath, 'Include Library', { + order: '1', + }); // `Manage Libraries...` group. registry.registerMenuAction([...includeLibMenuPath, '0_manage'], { commandId: `${LibraryListWidget.WIDGET_ID}:toggle`, - label: 'Manage Libraries...' + label: 'Manage Libraries...', }); } registerCommands(registry: CommandRegistry): void { registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, { - execute: async arg => { + execute: async (arg) => { if (LibraryPackage.is(arg)) { this.includeLibrary(arg); } - } + }, }); } @@ -72,13 +85,17 @@ export class IncludeLibrary extends SketchContribution { return this.queue.add(async () => { this.toDispose.dispose(); this.mainMenuManager.update(); - const libraries: LibraryPackage[] = [] - const fqbn = this.boardsServiceClient.boardsConfig.selectedBoard?.fqbn; + const libraries: LibraryPackage[] = []; + const fqbn = + this.boardsServiceClient.boardsConfig.selectedBoard?.fqbn; // Show all libraries, when no board is selected. // Otherwise, show libraries only for the selected board. - libraries.push(...await this.libraryService.list({ fqbn })); + libraries.push(...(await this.libraryService.list({ fqbn }))); - const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include']; + const includeLibMenuPath = [ + ...ArduinoMenus.SKETCH__UTILS_GROUP, + '0_include', + ]; // `Add .ZIP Library...` // TODO: implement it @@ -94,30 +111,50 @@ export class IncludeLibrary extends SketchContribution { } for (const library of user) { - this.toDispose.push(this.registerLibrary(library, userMenuPath)); + this.toDispose.push( + this.registerLibrary(library, userMenuPath) + ); } for (const library of rest) { - this.toDispose.push(this.registerLibrary(library, packageMenuPath)); + this.toDispose.push( + this.registerLibrary(library, packageMenuPath) + ); } this.mainMenuManager.update(); }); } - protected registerLibrary(libraryOrPlaceholder: LibraryPackage | string, menuPath: MenuPath): Disposable { + protected registerLibrary( + libraryOrPlaceholder: LibraryPackage | string, + menuPath: MenuPath + ): Disposable { if (typeof libraryOrPlaceholder === 'string') { - const placeholder = new PlaceholderMenuNode(menuPath, libraryOrPlaceholder); + const placeholder = new PlaceholderMenuNode( + menuPath, + libraryOrPlaceholder + ); this.menuRegistry.registerMenuNode(menuPath, placeholder); - return Disposable.create(() => this.menuRegistry.unregisterMenuNode(placeholder.id)); + return Disposable.create(() => + this.menuRegistry.unregisterMenuNode(placeholder.id) + ); } const commandId = `arduino-include-library--${libraryOrPlaceholder.name}:${libraryOrPlaceholder.author}`; const command = { id: commandId }; - const handler = { execute: () => this.commandRegistry.executeCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY.id, libraryOrPlaceholder) }; + const handler = { + execute: () => + this.commandRegistry.executeCommand( + IncludeLibrary.Commands.INCLUDE_LIBRARY.id, + libraryOrPlaceholder + ), + }; const menuAction = { commandId, label: libraryOrPlaceholder.name }; this.menuRegistry.registerMenuAction(menuPath, menuAction); return new DisposableCollection( this.commandRegistry.registerCommand(command, handler), - Disposable.create(() => this.menuRegistry.unregisterMenuAction(menuAction)), + Disposable.create(() => + this.menuRegistry.unregisterMenuAction(menuAction) + ) ); } @@ -131,13 +168,19 @@ export class IncludeLibrary extends SketchContribution { let codeEditor: monaco.editor.IStandaloneCodeEditor | undefined; const editor = this.editorManager.currentEditor?.editor; if (editor instanceof MonacoEditor) { - if (sketch.additionalFileUris.some(uri => uri === editor.uri.toString())) { + if ( + sketch.additionalFileUris.some( + (uri) => uri === editor.uri.toString() + ) + ) { codeEditor = editor.getControl(); } } if (!codeEditor) { - const widget = await this.editorManager.open(new URI(sketch.mainFileUri)); + const widget = await this.editorManager.open( + new URI(sketch.mainFileUri) + ); if (widget.editor instanceof MonacoEditor) { codeEditor = widget.editor.getControl(); } @@ -155,22 +198,29 @@ export class IncludeLibrary extends SketchContribution { const eol = textModel.getEOL(); const includes = library.includes.slice(); includes.push(''); // For the trailing new line. - const text = includes.map(include => include ? `#include <${include}>` : eol).join(eol); + const text = includes + .map((include) => (include ? `#include <${include}>` : eol)) + .join(eol); textModel.pushStackElement(); // Start a fresh operation. - textModel.pushEditOperations(cursorState, [{ - range: new monaco.Range(1, 1, 1, 1), - text, - forceMoveMarkers: true - }], () => cursorState); + textModel.pushEditOperations( + cursorState, + [ + { + range: new monaco.Range(1, 1, 1, 1), + text, + forceMoveMarkers: true, + }, + ], + () => cursorState + ); textModel.pushStackElement(); // Make it undoable. } - } export namespace IncludeLibrary { export namespace Commands { export const INCLUDE_LIBRARY: Command = { - id: 'arduino-include-library' + id: 'arduino-include-library', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/new-sketch.ts b/arduino-ide-extension/src/browser/contributions/new-sketch.ts index d788d2db..3a231a41 100644 --- a/arduino-ide-extension/src/browser/contributions/new-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/new-sketch.ts @@ -1,18 +1,27 @@ import { injectable } from 'inversify'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; -import { SketchContribution, URI, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry } from './contribution'; +import { + SketchContribution, + URI, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, +} from './contribution'; @injectable() export class NewSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { registry.registerCommand(NewSketch.Commands.NEW_SKETCH, { - execute: () => this.newSketch() + execute: () => this.newSketch(), }); registry.registerCommand(NewSketch.Commands.NEW_SKETCH__TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - execute: () => registry.executeCommand(NewSketch.Commands.NEW_SKETCH.id) + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', + execute: () => + registry.executeCommand(NewSketch.Commands.NEW_SKETCH.id), }); } @@ -20,14 +29,14 @@ export class NewSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: NewSketch.Commands.NEW_SKETCH.id, label: 'New', - order: '0' + order: '0', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: NewSketch.Commands.NEW_SKETCH.id, - keybinding: 'CtrlCmd+N' + keybinding: 'CtrlCmd+N', }); } @@ -36,7 +45,7 @@ export class NewSketch extends SketchContribution { id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, tooltip: 'New', - priority: 3 + priority: 3, }); } @@ -48,16 +57,15 @@ export class NewSketch extends SketchContribution { await this.messageService.error(e.toString()); } } - } export namespace NewSketch { export namespace Commands { export const NEW_SKETCH: Command = { - id: 'arduino-new-sketch' + id: 'arduino-new-sketch', }; export const NEW_SKETCH__TOOLBAR: Command = { - id: 'arduino-new-sketch--toolbar' + id: 'arduino-new-sketch--toolbar', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts index 1f482222..6a6be49e 100644 --- a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts @@ -1,7 +1,15 @@ import { inject, injectable } from 'inversify'; import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { SketchContribution, CommandRegistry, MenuModelRegistry, Sketch } from './contribution'; +import { + Disposable, + DisposableCollection, +} from '@theia/core/lib/common/disposable'; +import { + SketchContribution, + CommandRegistry, + MenuModelRegistry, + Sketch, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { MainMenuManager } from '../../common/main-menu-manager'; import { OpenSketch } from './open-sketch'; @@ -9,7 +17,6 @@ import { NotificationCenter } from '../notification-center'; @injectable() export class OpenRecentSketch extends SketchContribution { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -32,16 +39,22 @@ export class OpenRecentSketch extends SketchContribution { this.register(sketches); this.mainMenuManager.update(); }; - this.notificationCenter.onRecentSketchesChanged(({ sketches }) => refreshMenu(sketches)); + this.notificationCenter.onRecentSketchesChanged(({ sketches }) => + refreshMenu(sketches) + ); this.sketchService.recentlyOpenedSketches().then(refreshMenu); } registerMenus(registry: MenuModelRegistry): void { - registry.registerSubmenu(ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, 'Open Recent', { order: '2' }); + registry.registerSubmenu( + ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, + 'Open Recent', + { order: '2' } + ); } protected register(sketches: Sketch[]): void { - let order = 0; + const order = 0; for (const sketch of sketches) { const { uri } = sketch; const toDispose = this.toDisposeBeforeRegister.get(uri); @@ -49,14 +62,33 @@ export class OpenRecentSketch extends SketchContribution { toDispose.dispose(); } const command = { id: `arduino-open-recent--${uri}` }; - const handler = { execute: () => this.commandRegistry.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch) }; + const handler = { + execute: () => + this.commandRegistry.executeCommand( + OpenSketch.Commands.OPEN_SKETCH.id, + sketch + ), + }; this.commandRegistry.registerCommand(command, handler); - this.menuRegistry.registerMenuAction(ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, { commandId: command.id, label: sketch.name, order: String(order) }); - this.toDisposeBeforeRegister.set(sketch.uri, new DisposableCollection( - Disposable.create(() => this.commandRegistry.unregisterCommand(command)), - Disposable.create(() => this.menuRegistry.unregisterMenuAction(command)) - )); + this.menuRegistry.registerMenuAction( + ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, + { + commandId: command.id, + label: sketch.name, + order: String(order), + } + ); + this.toDisposeBeforeRegister.set( + sketch.uri, + new DisposableCollection( + Disposable.create(() => + this.commandRegistry.unregisterCommand(command) + ), + Disposable.create(() => + this.menuRegistry.unregisterMenuAction(command) + ) + ) + ); } } - } diff --git a/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts b/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts index b2711e96..b43a93b8 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts @@ -2,14 +2,19 @@ import { injectable } from 'inversify'; import { remote } from 'electron'; import URI from '@theia/core/lib/common/uri'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, +} from './contribution'; @injectable() export class OpenSketchExternal extends SketchContribution { - registerCommands(registry: CommandRegistry): void { registry.registerCommand(OpenSketchExternal.Commands.OPEN_EXTERNAL, { - execute: () => this.openExternal() + execute: () => this.openExternal(), }); } @@ -17,14 +22,14 @@ export class OpenSketchExternal extends SketchContribution { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, label: 'Show Sketch Folder', - order: '0' + order: '0', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, - keybinding: 'CtrlCmd+Alt+K' + keybinding: 'CtrlCmd+Alt+K', }); } @@ -40,13 +45,12 @@ export class OpenSketchExternal extends SketchContribution { } } } - } export namespace OpenSketchExternal { export namespace Commands { export const OPEN_EXTERNAL: Command = { - id: 'arduino-open-sketch-external' + id: 'arduino-open-sketch-external', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/open-sketch.ts b/arduino-ide-extension/src/browser/contributions/open-sketch.ts index 5d6f7412..300f7fba 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch.ts @@ -2,10 +2,22 @@ import { inject, injectable } from 'inversify'; import { remote } from 'electron'; import { MaybePromise } from '@theia/core/lib/common/types'; import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; +import { + Disposable, + DisposableCollection, +} from '@theia/core/lib/common/disposable'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; -import { SketchContribution, Sketch, URI, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry } from './contribution'; +import { + SketchContribution, + Sketch, + URI, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, +} from './contribution'; import { ExamplesService } from '../../common/protocol/examples-service'; import { BuiltInExamples } from './examples'; import { Sketchbook } from './sketchbook'; @@ -13,7 +25,6 @@ import { SketchContainer } from '../../common/protocol'; @injectable() export class OpenSketch extends SketchContribution { - @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry; @@ -33,12 +44,16 @@ export class OpenSketch extends SketchContribution { registerCommands(registry: CommandRegistry): void { registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH, { - execute: arg => Sketch.is(arg) ? this.openSketch(arg) : this.openSketch() + execute: (arg) => + Sketch.is(arg) ? this.openSketch(arg) : this.openSketch(), }); registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH__TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', execute: async (_: Widget, target: EventTarget) => { - const container = await this.sketchService.getSketches({ exclude: ['**/hardware/**'] }); + const container = await this.sketchService.getSketches({ + exclude: ['**/hardware/**'], + }); if (SketchContainer.isEmpty(container)) { this.openSketch(); } else { @@ -51,30 +66,53 @@ export class OpenSketch extends SketchContribution { return; } - this.menuRegistry.registerMenuAction(ArduinoMenus.OPEN_SKETCH__CONTEXT__OPEN_GROUP, { - commandId: OpenSketch.Commands.OPEN_SKETCH.id, - label: 'Open...' - }); - this.toDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(OpenSketch.Commands.OPEN_SKETCH))); - this.sketchbook.registerRecursively([...container.children, ...container.sketches], ArduinoMenus.OPEN_SKETCH__CONTEXT__RECENT_GROUP, this.toDispose); + this.menuRegistry.registerMenuAction( + ArduinoMenus.OPEN_SKETCH__CONTEXT__OPEN_GROUP, + { + commandId: OpenSketch.Commands.OPEN_SKETCH.id, + label: 'Open...', + } + ); + this.toDispose.push( + Disposable.create(() => + this.menuRegistry.unregisterMenuAction( + OpenSketch.Commands.OPEN_SKETCH + ) + ) + ); + this.sketchbook.registerRecursively( + [...container.children, ...container.sketches], + ArduinoMenus.OPEN_SKETCH__CONTEXT__RECENT_GROUP, + this.toDispose + ); try { - const containers = await this.examplesService.builtIns(); + const containers = + await this.examplesService.builtIns(); for (const container of containers) { - this.builtInExamples.registerRecursively(container, ArduinoMenus.OPEN_SKETCH__CONTEXT__EXAMPLES_GROUP, this.toDispose); + this.builtInExamples.registerRecursively( + container, + ArduinoMenus.OPEN_SKETCH__CONTEXT__EXAMPLES_GROUP, + this.toDispose + ); } } catch (e) { - console.error('Error when collecting built-in examples.', e); + console.error( + 'Error when collecting built-in examples.', + e + ); } const options = { menuPath: ArduinoMenus.OPEN_SKETCH__CONTEXT, anchor: { x: parentElement.getBoundingClientRect().left, - y: parentElement.getBoundingClientRect().top + parentElement.offsetHeight - } - } + y: + parentElement.getBoundingClientRect().top + + parentElement.offsetHeight, + }, + }; this.contextMenuRenderer.render(options); } - } + }, }); } @@ -82,14 +120,14 @@ export class OpenSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: OpenSketch.Commands.OPEN_SKETCH.id, label: 'Open...', - order: '1' + order: '1', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: OpenSketch.Commands.OPEN_SKETCH.id, - keybinding: 'CtrlCmd+O' + keybinding: 'CtrlCmd+O', }); } @@ -98,11 +136,13 @@ export class OpenSketch extends SketchContribution { id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, tooltip: 'Open', - priority: 4 + priority: 4, }); } - async openSketch(toOpen: MaybePromise = this.selectSketch()): Promise { + async openSketch( + toOpen: MaybePromise = this.selectSketch() + ): Promise { const sketch = await toOpen; if (sketch) { this.workspaceService.open(new URI(sketch.uri)); @@ -111,22 +151,26 @@ export class OpenSketch extends SketchContribution { protected async selectSketch(): Promise { const config = await this.configService.getConfiguration(); - const defaultPath = await this.fileService.fsPath(new URI(config.sketchDirUri)); + const defaultPath = await this.fileService.fsPath( + new URI(config.sketchDirUri) + ); const { filePaths } = await remote.dialog.showOpenDialog({ defaultPath, properties: ['createDirectory', 'openFile'], filters: [ { name: 'Sketch', - extensions: ['ino', 'pde'] - } - ] + extensions: ['ino', 'pde'], + }, + ], }); if (!filePaths.length) { return undefined; } if (filePaths.length > 1) { - this.logger.warn(`Multiple sketches were selected: ${filePaths}. Using the first one.`); + this.logger.warn( + `Multiple sketches were selected: ${filePaths}. Using the first one.` + ); } const sketchFilePath = filePaths[0]; const sketchFileUri = await this.fileSystemExt.getUri(sketchFilePath); @@ -136,40 +180,49 @@ export class OpenSketch extends SketchContribution { } if (Sketch.isSketchFile(sketchFileUri)) { const name = new URI(sketchFileUri).path.name; - const nameWithExt = this.labelProvider.getName(new URI(sketchFileUri)); + const nameWithExt = this.labelProvider.getName( + new URI(sketchFileUri) + ); const { response } = await remote.dialog.showMessageBox({ title: 'Moving', type: 'question', buttons: ['Cancel', 'OK'], - message: `The file "${nameWithExt}" needs to be inside a sketch folder named as "${name}".\nCreate this folder, move the file, and continue?` + message: `The file "${nameWithExt}" needs to be inside a sketch folder named as "${name}".\nCreate this folder, move the file, and continue?`, }); - if (response === 1) { // OK - const newSketchUri = new URI(sketchFileUri).parent.resolve(name); + if (response === 1) { + // OK + const newSketchUri = new URI(sketchFileUri).parent.resolve( + name + ); const exists = await this.fileService.exists(newSketchUri); if (exists) { await remote.dialog.showMessageBox({ type: 'error', title: 'Error', - message: `A folder named "${name}" already exists. Can't open sketch.` + message: `A folder named "${name}" already exists. Can't open sketch.`, }); return undefined; } await this.fileService.createFolder(newSketchUri); - await this.fileService.move(new URI(sketchFileUri), new URI(newSketchUri.resolve(nameWithExt).toString())); - return this.sketchService.getSketchFolder(newSketchUri.toString()); + await this.fileService.move( + new URI(sketchFileUri), + new URI(newSketchUri.resolve(nameWithExt).toString()) + ); + return this.sketchService.getSketchFolder( + newSketchUri.toString() + ); } } } - } export namespace OpenSketch { export namespace Commands { export const OPEN_SKETCH: Command = { - id: 'arduino-open-sketch' + id: 'arduino-open-sketch', }; export const OPEN_SKETCH__TOOLBAR: Command = { - id: 'arduino-open-sketch--toolbar' + id: 'arduino-open-sketch--toolbar', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/quit-app.ts b/arduino-ide-extension/src/browser/contributions/quit-app.ts index bcda4a66..e2ceaf32 100644 --- a/arduino-ide-extension/src/browser/contributions/quit-app.ts +++ b/arduino-ide-extension/src/browser/contributions/quit-app.ts @@ -1,27 +1,32 @@ import { injectable } from 'inversify'; import { remote } from 'electron'; import { isOSX } from '@theia/core/lib/common/os'; -import { Contribution, Command, MenuModelRegistry, KeybindingRegistry, CommandRegistry } from './contribution'; +import { + Contribution, + Command, + MenuModelRegistry, + KeybindingRegistry, + CommandRegistry, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; @injectable() export class QuitApp extends Contribution { - registerCommands(registry: CommandRegistry): void { if (!isOSX) { registry.registerCommand(QuitApp.Commands.QUIT_APP, { - execute: () => remote.app.quit() + execute: () => remote.app.quit(), }); } } registerMenus(registry: MenuModelRegistry): void { - // On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it. + // On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it. if (!isOSX) { registry.registerMenuAction(ArduinoMenus.FILE__QUIT_GROUP, { commandId: QuitApp.Commands.QUIT_APP.id, label: 'Quit', - order: '0' + order: '0', }); } } @@ -30,17 +35,16 @@ export class QuitApp extends Contribution { if (!isOSX) { registry.registerKeybinding({ command: QuitApp.Commands.QUIT_APP.id, - keybinding: 'CtrlCmd+Q' + keybinding: 'CtrlCmd+Q', }); } } - } export namespace QuitApp { export namespace Commands { export const QUIT_APP: Command = { - id: 'arduino-quit-app' + id: 'arduino-quit-app', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts b/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts index 3b289bb4..a270a2dc 100644 --- a/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts @@ -2,14 +2,20 @@ import { injectable } from 'inversify'; import { remote } from 'electron'; import * as dateFormat from 'dateformat'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { SketchContribution, URI, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry } from './contribution'; +import { + SketchContribution, + URI, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, +} from './contribution'; @injectable() export class SaveAsSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, { - execute: args => this.saveAs(args) + execute: (args) => this.saveAs(args), }); } @@ -17,21 +23,27 @@ export class SaveAsSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, label: 'Save As...', - order: '7' + order: '7', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, - keybinding: 'CtrlCmd+Shift+S' + keybinding: 'CtrlCmd+Shift+S', }); } /** * Resolves `true` if the sketch was successfully saved as something. */ - async saveAs({ execOnlyIfTemp, openAfterMove, wipeOriginal }: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT): Promise { + async saveAs( + { + execOnlyIfTemp, + openAfterMove, + wipeOriginal, + }: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT + ): Promise { const sketch = await this.sketchServiceClient.currentSketch(); if (!sketch) { return false; @@ -44,13 +56,29 @@ export class SaveAsSketch extends SketchContribution { // If target does not exist, propose a `directories.user`/${sketch.name} path // If target exists, propose `directories.user`/${sketch.name}_copy_${yyyymmddHHMMss} - const sketchDirUri = new URI((await this.configService.getConfiguration()).sketchDirUri); - const exists = await this.fileService.exists(sketchDirUri.resolve(sketch.name)); + const sketchDirUri = new URI( + (await this.configService.getConfiguration()).sketchDirUri + ); + const exists = await this.fileService.exists( + sketchDirUri.resolve(sketch.name) + ); const defaultUri = exists - ? sketchDirUri.resolve(sketchDirUri.resolve(`${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`).toString()) + ? sketchDirUri.resolve( + sketchDirUri + .resolve( + `${sketch.name}_copy_${dateFormat( + new Date(), + 'yyyymmddHHMMss' + )}` + ) + .toString() + ) : sketchDirUri.resolve(sketch.name); const defaultPath = await this.fileService.fsPath(defaultUri); - const { filePath, canceled } = await remote.dialog.showSaveDialog({ title: 'Save sketch folder as...', defaultPath }); + const { filePath, canceled } = await remote.dialog.showSaveDialog({ + title: 'Save sketch folder as...', + defaultPath, + }); if (!filePath || canceled) { return false; } @@ -58,24 +86,31 @@ export class SaveAsSketch extends SketchContribution { if (!destinationUri) { return false; } - const workspaceUri = await this.sketchService.copy(sketch, { destinationUri }); + const workspaceUri = await this.sketchService.copy(sketch, { + destinationUri, + }); if (workspaceUri && openAfterMove) { if (wipeOriginal || (openAfterMove && execOnlyIfTemp)) { try { - await this.fileService.delete(new URI(sketch.uri), { recursive: true }); - } catch { /* NOOP: from time to time, it's not possible to wipe the old resource from the temp dir on Windows */ } + await this.fileService.delete(new URI(sketch.uri), { + recursive: true, + }); + } catch { + /* NOOP: from time to time, it's not possible to wipe the old resource from the temp dir on Windows */ + } } - this.workspaceService.open(new URI(workspaceUri), { preserveWindow: true }); + this.workspaceService.open(new URI(workspaceUri), { + preserveWindow: true, + }); } return !!workspaceUri; } - } export namespace SaveAsSketch { export namespace Commands { export const SAVE_AS_SKETCH: Command = { - id: 'arduino-save-as-sketch' + id: 'arduino-save-as-sketch', }; } export interface Options { @@ -90,7 +125,7 @@ export namespace SaveAsSketch { export const DEFAULT: Options = { execOnlyIfTemp: false, openAfterMove: true, - wipeOriginal: false + wipeOriginal: false, }; } } diff --git a/arduino-ide-extension/src/browser/contributions/save-sketch.ts b/arduino-ide-extension/src/browser/contributions/save-sketch.ts index a047d5ca..b5b382b1 100644 --- a/arduino-ide-extension/src/browser/contributions/save-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/save-sketch.ts @@ -2,18 +2,26 @@ import { injectable } from 'inversify'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, +} from './contribution'; @injectable() export class SaveSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH, { - execute: () => this.saveSketch() + execute: () => this.saveSketch(), }); registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH__TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - execute: () => registry.executeCommand(SaveSketch.Commands.SAVE_SKETCH.id) + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', + execute: () => + registry.executeCommand(SaveSketch.Commands.SAVE_SKETCH.id), }); } @@ -21,14 +29,14 @@ export class SaveSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: SaveSketch.Commands.SAVE_SKETCH.id, label: 'Save', - order: '6' + order: '6', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: SaveSketch.Commands.SAVE_SKETCH.id, - keybinding: 'CtrlCmd+S' + keybinding: 'CtrlCmd+S', }); } @@ -37,23 +45,22 @@ export class SaveSketch extends SketchContribution { id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, tooltip: 'Save', - priority: 5 + priority: 5, }); } async saveSketch(): Promise { return this.commandService.executeCommand(CommonCommands.SAVE_ALL.id); } - } export namespace SaveSketch { export namespace Commands { export const SAVE_SKETCH: Command = { - id: 'arduino-save-sketch' + id: 'arduino-save-sketch', }; export const SAVE_SKETCH__TOOLBAR: Command = { - id: 'arduino-save-sketch--toolbar' + id: 'arduino-save-sketch--toolbar', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/settings.ts b/arduino-ide-extension/src/browser/contributions/settings.ts index a57c0b69..9878843b 100644 --- a/arduino-ide-extension/src/browser/contributions/settings.ts +++ b/arduino-ide-extension/src/browser/contributions/settings.ts @@ -1,11 +1,16 @@ import { inject, injectable } from 'inversify'; -import { Command, MenuModelRegistry, CommandRegistry, SketchContribution, KeybindingRegistry } from './contribution'; +import { + Command, + MenuModelRegistry, + CommandRegistry, + SketchContribution, + KeybindingRegistry, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { Settings as Preferences, SettingsDialog } from '../settings'; @injectable() export class Settings extends SketchContribution { - @inject(SettingsDialog) protected readonly settingsDialog: SettingsDialog; @@ -28,7 +33,7 @@ export class Settings extends SketchContribution { await this.settingsService.reset(); } }, - isEnabled: () => !this.settingsOpened + isEnabled: () => !this.settingsOpened, }); } @@ -36,9 +41,12 @@ export class Settings extends SketchContribution { registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, { commandId: Settings.Commands.OPEN.id, label: 'Preferences...', - order: '0' + order: '0', }); - registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced'); + registry.registerSubmenu( + ArduinoMenus.FILE__ADVANCED_SUBMENU, + 'Advanced' + ); } registerKeybindings(registry: KeybindingRegistry): void { @@ -47,7 +55,6 @@ export class Settings extends SketchContribution { keybinding: 'CtrlCmd+,', }); } - } export namespace Settings { @@ -55,7 +62,7 @@ export namespace Settings { export const OPEN: Command = { id: 'arduino-settings-open', label: 'Open Preferences...', - category: 'Arduino' - } + category: 'Arduino', + }; } } diff --git a/arduino-ide-extension/src/browser/contributions/sketch-control.ts b/arduino-ide-extension/src/browser/contributions/sketch-control.ts index 6e4010ce..5f8d6034 100644 --- a/arduino-ide-extension/src/browser/contributions/sketch-control.ts +++ b/arduino-ide-extension/src/browser/contributions/sketch-control.ts @@ -3,13 +3,24 @@ import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribu import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { WorkspaceCommands } from '@theia/workspace/lib/browser'; import { ContextMenuRenderer } from '@theia/core/lib/browser/context-menu-renderer'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { URI, SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry, open } from './contribution'; +import { + Disposable, + DisposableCollection, +} from '@theia/core/lib/common/disposable'; +import { + URI, + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, + open, +} from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; @injectable() export class SketchControl extends SketchContribution { - @inject(ApplicationShell) protected readonly shell: ApplicationShell; @@ -19,111 +30,149 @@ export class SketchControl extends SketchContribution { @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; - protected readonly toDisposeBeforeCreateNewContextMenu = new DisposableCollection(); + protected readonly toDisposeBeforeCreateNewContextMenu = + new DisposableCollection(); registerCommands(registry: CommandRegistry): void { - registry.registerCommand(SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR, { - isVisible: widget => this.shell.getWidgets('main').indexOf(widget) !== -1, - execute: async () => { - this.toDisposeBeforeCreateNewContextMenu.dispose(); - const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { - return; - } - - const target = document.getElementById(SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id); - if (!(target instanceof HTMLElement)) { - return; - } - const { parentElement } = target; - if (!parentElement) { - return; - } - - const { mainFileUri, rootFolderFileUris } = await this.sketchService.loadSketch(sketch.uri); - const uris = [mainFileUri, ...rootFolderFileUris]; - for (let i = 0; i < uris.length; i++) { - const uri = new URI(uris[i]); - const command = { id: `arduino-focus-file--${uri.toString()}` }; - const handler = { execute: () => open(this.openerService, uri) }; - this.toDisposeBeforeCreateNewContextMenu.push(registry.registerCommand(command, handler)); - this.menuRegistry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__RESOURCES_GROUP, { - commandId: command.id, - label: this.labelProvider.getName(uri), - order: `${i}` - }); - this.toDisposeBeforeCreateNewContextMenu.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(command))); - } - const options = { - menuPath: ArduinoMenus.SKETCH_CONTROL__CONTEXT, - anchor: { - x: parentElement.getBoundingClientRect().left, - y: parentElement.getBoundingClientRect().top + parentElement.offsetHeight + registry.registerCommand( + SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR, + { + isVisible: (widget) => + this.shell.getWidgets('main').indexOf(widget) !== -1, + execute: async () => { + this.toDisposeBeforeCreateNewContextMenu.dispose(); + const sketch = + await this.sketchServiceClient.currentSketch(); + if (!sketch) { + return; } - } - this.contextMenuRenderer.render(options); + + const target = document.getElementById( + SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id + ); + if (!(target instanceof HTMLElement)) { + return; + } + const { parentElement } = target; + if (!parentElement) { + return; + } + + const { mainFileUri, rootFolderFileUris } = + await this.sketchService.loadSketch(sketch.uri); + const uris = [mainFileUri, ...rootFolderFileUris]; + for (let i = 0; i < uris.length; i++) { + const uri = new URI(uris[i]); + const command = { + id: `arduino-focus-file--${uri.toString()}`, + }; + const handler = { + execute: () => open(this.openerService, uri), + }; + this.toDisposeBeforeCreateNewContextMenu.push( + registry.registerCommand(command, handler) + ); + this.menuRegistry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__RESOURCES_GROUP, + { + commandId: command.id, + label: this.labelProvider.getName(uri), + order: `${i}`, + } + ); + this.toDisposeBeforeCreateNewContextMenu.push( + Disposable.create(() => + this.menuRegistry.unregisterMenuAction(command) + ) + ); + } + const options = { + menuPath: ArduinoMenus.SKETCH_CONTROL__CONTEXT, + anchor: { + x: parentElement.getBoundingClientRect().left, + y: + parentElement.getBoundingClientRect().top + + parentElement.offsetHeight, + }, + }; + this.contextMenuRenderer.render(options); + }, } - }); + ); } registerMenus(registry: MenuModelRegistry): void { - registry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, { - commandId: WorkspaceCommands.NEW_FILE.id, - label: 'New Tab', - order: '0' - }); - registry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, { - commandId: WorkspaceCommands.FILE_RENAME.id, - label: 'Rename', - order: '1' - }); - registry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, { - commandId: WorkspaceCommands.FILE_DELETE.id, // TODO: customize delete. Wipe sketch if deleting main file. Close window. - label: 'Delete', - order: '2' - }); + registry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, + { + commandId: WorkspaceCommands.NEW_FILE.id, + label: 'New Tab', + order: '0', + } + ); + registry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, + { + commandId: WorkspaceCommands.FILE_RENAME.id, + label: 'Rename', + order: '1', + } + ); + registry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, + { + commandId: WorkspaceCommands.FILE_DELETE.id, // TODO: customize delete. Wipe sketch if deleting main file. Close window. + label: 'Delete', + order: '2', + } + ); - registry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP, { - commandId: CommonCommands.PREVIOUS_TAB.id, - label: 'Previous Tab', - order: '0' - }); - registry.registerMenuAction(ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP, { - commandId: CommonCommands.NEXT_TAB.id, - label: 'Next Tab', - order: '0' - }); + registry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP, + { + commandId: CommonCommands.PREVIOUS_TAB.id, + label: 'Previous Tab', + order: '0', + } + ); + registry.registerMenuAction( + ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP, + { + commandId: CommonCommands.NEXT_TAB.id, + label: 'Next Tab', + order: '0', + } + ); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: WorkspaceCommands.NEW_FILE.id, - keybinding: 'CtrlCmd+Shift+N' + keybinding: 'CtrlCmd+Shift+N', }); registry.registerKeybinding({ command: CommonCommands.PREVIOUS_TAB.id, - keybinding: 'CtrlCmd+Alt+Left' // TODO: check why electron does not show the keybindings in the UI. + keybinding: 'CtrlCmd+Alt+Left', // TODO: check why electron does not show the keybindings in the UI. }); registry.registerKeybinding({ command: CommonCommands.NEXT_TAB.id, - keybinding: 'CtrlCmd+Alt+Right' + keybinding: 'CtrlCmd+Alt+Right', }); } registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, - command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id + command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, }); } - } export namespace SketchControl { export namespace Commands { export const OPEN_SKETCH_CONTROL__TOOLBAR: Command = { id: 'arduino-open-sketch-control--toolbar', - iconClass: 'fa fa-caret-down' + iconClass: 'fa fa-caret-down', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/sketchbook.ts b/arduino-ide-extension/src/browser/contributions/sketchbook.ts index 117d0709..8b5976d6 100644 --- a/arduino-ide-extension/src/browser/contributions/sketchbook.ts +++ b/arduino-ide-extension/src/browser/contributions/sketchbook.ts @@ -10,7 +10,6 @@ import { OpenSketch } from './open-sketch'; @injectable() export class Sketchbook extends Examples { - @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -24,12 +23,12 @@ export class Sketchbook extends Examples { protected readonly notificationCenter: NotificationCenter; onStart(): void { - this.sketchService.getSketches({}).then(container => { + this.sketchService.getSketches({}).then((container) => { this.register(container); this.mainMenuManager.update(); }); this.sketchServiceClient.onSketchbookDidChange(() => { - this.sketchService.getSketches({}).then(container => { + this.sketchService.getSketches({}).then((container) => { this.register(container); this.mainMenuManager.update(); }); @@ -37,21 +36,31 @@ export class Sketchbook extends Examples { } registerMenus(registry: MenuModelRegistry): void { - registry.registerSubmenu(ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, 'Sketchbook', { order: '3' }); + registry.registerSubmenu( + ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, + 'Sketchbook', + { order: '3' } + ); } protected register(container: SketchContainer): void { this.toDispose.dispose(); - this.registerRecursively([...container.children, ...container.sketches], ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, this.toDispose); + this.registerRecursively( + [...container.children, ...container.sketches], + ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, + this.toDispose + ); } protected createHandler(uri: string): CommandHandler { return { execute: async () => { const sketch = await this.sketchService.loadSketch(uri); - return this.commandService.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch); - } - } + return this.commandService.executeCommand( + OpenSketch.Commands.OPEN_SKETCH.id, + sketch + ); + }, + }; } - } diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index 988ca0dc..5332dbea 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -6,11 +6,17 @@ import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { BoardsDataStore } from '../boards/boards-data-store'; import { MonitorConnection } from '../monitor/monitor-connection'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, +} from './contribution'; @injectable() export class UploadSketch extends SketchContribution { - @inject(CoreService) protected readonly coreService: CoreService; @@ -33,15 +39,20 @@ export class UploadSketch extends SketchContribution { execute: () => this.uploadSketch(), isEnabled: () => !this.uploadInProgress, }); - registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER, { - execute: () => this.uploadSketch(true), - isEnabled: () => !this.uploadInProgress, - }); + registry.registerCommand( + UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER, + { + execute: () => this.uploadSketch(true), + isEnabled: () => !this.uploadInProgress, + } + ); registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', isEnabled: () => !this.uploadInProgress, isToggled: () => this.uploadInProgress, - execute: () => registry.executeCommand(UploadSketch.Commands.UPLOAD_SKETCH.id) + execute: () => + registry.executeCommand(UploadSketch.Commands.UPLOAD_SKETCH.id), }); } @@ -49,23 +60,23 @@ export class UploadSketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: UploadSketch.Commands.UPLOAD_SKETCH.id, label: 'Upload', - order: '1' + order: '1', }); registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER.id, label: 'Upload Using Programmer', - order: '2' + order: '2', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: UploadSketch.Commands.UPLOAD_SKETCH.id, - keybinding: 'CtrlCmd+U' + keybinding: 'CtrlCmd+U', }); registry.registerKeybinding({ command: UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER.id, - keybinding: 'CtrlCmd+Shift+U' + keybinding: 'CtrlCmd+Shift+U', }); } @@ -75,12 +86,11 @@ export class UploadSketch extends SketchContribution { command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id, tooltip: 'Upload', priority: 1, - onDidChange: this.onDidChange + onDidChange: this.onDidChange, }); } - async uploadSketch(usingProgrammer: boolean = false): Promise { - + async uploadSketch(usingProgrammer = false): Promise { // even with buttons disabled, better to double check if an upload is already in progress if (this.uploadInProgress) { return; @@ -105,12 +115,20 @@ export class UploadSketch extends SketchContribution { } try { const { boardsConfig } = this.boardsServiceClientImpl; - const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] = await Promise.all([ - this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn), + const [ + fqbn, + { selectedProgrammer }, + verify, + verbose, + sourceOverride, + ] = await Promise.all([ + this.boardsDataStore.appendConfigToFqbn( + boardsConfig.selectedBoard?.fqbn + ), this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn), this.preferences.get('arduino.upload.verify'), this.preferences.get('arduino.upload.verbose'), - this.sourceOverride() + this.sourceOverride(), ]); let options: CoreService.Upload.Options | undefined = undefined; @@ -129,7 +147,7 @@ export class UploadSketch extends SketchContribution { port, verbose, verify, - sourceOverride + sourceOverride, }; } else { options = { @@ -139,7 +157,7 @@ export class UploadSketch extends SketchContribution { port, verbose, verify, - sourceOverride + sourceOverride, }; } this.outputChannelManager.getChannel('Arduino').clear(); @@ -158,7 +176,10 @@ export class UploadSketch extends SketchContribution { if (monitorConfig) { const { board, port } = monitorConfig; try { - await this.boardsServiceClientImpl.waitUntilAvailable(Object.assign(board, { port }), 10_000); + await this.boardsServiceClientImpl.waitUntilAvailable( + Object.assign(board, { port }), + 10_000 + ); if (shouldAutoConnect) { // Enabling auto-connect will trigger a connect. this.monitorConnection.autoConnect = true; @@ -166,24 +187,25 @@ export class UploadSketch extends SketchContribution { await this.monitorConnection.connect(monitorConfig); } } catch (waitError) { - this.messageService.error(`Could not reconnect to serial monitor. ${waitError.toString()}`); + this.messageService.error( + `Could not reconnect to serial monitor. ${waitError.toString()}` + ); } } } } - } export namespace UploadSketch { export namespace Commands { export const UPLOAD_SKETCH: Command = { - id: 'arduino-upload-sketch' + id: 'arduino-upload-sketch', }; export const UPLOAD_SKETCH_USING_PROGRAMMER: Command = { - id: 'arduino-upload-sketch-using-programmer' + id: 'arduino-upload-sketch-using-programmer', }; export const UPLOAD_SKETCH_TOOLBAR: Command = { - id: 'arduino-upload-sketch--toolbar' + id: 'arduino-upload-sketch--toolbar', }; } } diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index c525347c..55609bf6 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -5,11 +5,17 @@ import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { BoardsDataStore } from '../boards/boards-data-store'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, TabBarToolbarRegistry } from './contribution'; +import { + SketchContribution, + Command, + CommandRegistry, + MenuModelRegistry, + KeybindingRegistry, + TabBarToolbarRegistry, +} from './contribution'; @injectable() export class VerifySketch extends SketchContribution { - @inject(CoreService) protected readonly coreService: CoreService; @@ -34,10 +40,12 @@ export class VerifySketch extends SketchContribution { isEnabled: () => !this.verifyInProgress, }); registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'left', isEnabled: () => !this.verifyInProgress, isToggled: () => this.verifyInProgress, - execute: () => registry.executeCommand(VerifySketch.Commands.VERIFY_SKETCH.id) + execute: () => + registry.executeCommand(VerifySketch.Commands.VERIFY_SKETCH.id), }); } @@ -45,23 +53,23 @@ export class VerifySketch extends SketchContribution { registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: VerifySketch.Commands.VERIFY_SKETCH.id, label: 'Verify/Compile', - order: '0' + order: '0', }); registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: VerifySketch.Commands.EXPORT_BINARIES.id, label: 'Export compiled Binary', - order: '3' + order: '3', }); } registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: VerifySketch.Commands.VERIFY_SKETCH.id, - keybinding: 'CtrlCmd+R' + keybinding: 'CtrlCmd+R', }); registry.registerKeybinding({ command: VerifySketch.Commands.EXPORT_BINARIES.id, - keybinding: 'CtrlCmd+Alt+S' + keybinding: 'CtrlCmd+Alt+S', }); } @@ -71,34 +79,37 @@ export class VerifySketch extends SketchContribution { command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id, tooltip: 'Verify', priority: 0, - onDidChange: this.onDidChange + onDidChange: this.onDidChange, }); } async verifySketch(exportBinaries?: boolean): Promise { - // even with buttons disabled, better to double check if a verify is already in progress if (this.verifyInProgress) { return; } - + // toggle the toolbar button and menu item state. // verifyInProgress will be set to false whether the compilation fails or not this.verifyInProgress = true; this.onDidChangeEmitter.fire(); const sketch = await this.sketchServiceClient.currentSketch(); - + if (!sketch) { return; } try { const { boardsConfig } = this.boardsServiceClientImpl; const [fqbn, sourceOverride] = await Promise.all([ - this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn), - this.sourceOverride() + this.boardsDataStore.appendConfigToFqbn( + boardsConfig.selectedBoard?.fqbn + ), + this.sourceOverride(), ]); const verbose = this.preferences.get('arduino.compile.verbose'); - const compilerWarnings = this.preferences.get('arduino.compile.warnings'); + const compilerWarnings = this.preferences.get( + 'arduino.compile.warnings' + ); this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.compile({ sketchUri: sketch.uri, @@ -107,7 +118,7 @@ export class VerifySketch extends SketchContribution { verbose, exportBinaries, sourceOverride, - compilerWarnings + compilerWarnings, }); this.messageService.info('Done compiling.', { timeout: 3000 }); } catch (e) { @@ -117,19 +128,18 @@ export class VerifySketch extends SketchContribution { this.onDidChangeEmitter.fire(); } } - } export namespace VerifySketch { export namespace Commands { export const VERIFY_SKETCH: Command = { - id: 'arduino-verify-sketch' + id: 'arduino-verify-sketch', }; export const EXPORT_BINARIES: Command = { - id: 'arduino-export-binaries' + id: 'arduino-export-binaries', }; export const VERIFY_SKETCH_TOOLBAR: Command = { - id: 'arduino-verify-sketch--toolbar' + id: 'arduino-verify-sketch--toolbar', }; } } diff --git a/arduino-ide-extension/src/browser/editor-mode.ts b/arduino-ide-extension/src/browser/editor-mode.ts index 28ee6342..19b3aba2 100644 --- a/arduino-ide-extension/src/browser/editor-mode.ts +++ b/arduino-ide-extension/src/browser/editor-mode.ts @@ -1,10 +1,12 @@ import { injectable, inject } from 'inversify'; -import { FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser'; +import { + FrontendApplicationContribution, + FrontendApplication, +} from '@theia/core/lib/browser'; import { MainMenuManager } from '../common/main-menu-manager'; @injectable() export class EditorMode implements FrontendApplicationContribution { - @inject(MainMenuManager) protected readonly mainMenuManager: MainMenuManager; @@ -15,17 +17,21 @@ export class EditorMode implements FrontendApplicationContribution { } get compileForDebug(): boolean { - const value = window.localStorage.getItem(EditorMode.COMPILE_FOR_DEBUG_KEY); + const value = window.localStorage.getItem( + EditorMode.COMPILE_FOR_DEBUG_KEY + ); return value === 'true'; } async toggleCompileForDebug(): Promise { const oldState = this.compileForDebug; const newState = !oldState; - window.localStorage.setItem(EditorMode.COMPILE_FOR_DEBUG_KEY, String(newState)); + window.localStorage.setItem( + EditorMode.COMPILE_FOR_DEBUG_KEY, + String(newState) + ); this.mainMenuManager.update(); } - } export namespace EditorMode { diff --git a/arduino-ide-extension/src/browser/library/library-list-widget.ts b/arduino-ide-extension/src/browser/library/library-list-widget.ts index 4d254e51..e6136a01 100644 --- a/arduino-ide-extension/src/browser/library/library-list-widget.ts +++ b/arduino-ide-extension/src/browser/library/library-list-widget.ts @@ -2,21 +2,24 @@ import { injectable, postConstruct, inject } from 'inversify'; import { Message } from '@phosphor/messaging'; import { addEventListener } from '@theia/core/lib/browser/widgets/widget'; import { AbstractDialog, DialogProps } from '@theia/core/lib/browser/dialogs'; -import { LibraryPackage, LibraryService } from '../../common/protocol/library-service'; +import { + LibraryPackage, + LibraryService, +} from '../../common/protocol/library-service'; import { ListWidget } from '../widgets/component-list/list-widget'; import { Installable } from '../../common/protocol'; import { ListItemRenderer } from '../widgets/component-list/list-item-renderer'; @injectable() export class LibraryListWidget extends ListWidget { - static WIDGET_ID = 'library-list-widget'; static WIDGET_LABEL = 'Library Manager'; constructor( @inject(LibraryService) protected service: LibraryService, - @inject(ListItemRenderer) protected itemRenderer: ListItemRenderer) { - + @inject(ListItemRenderer) + protected itemRenderer: ListItemRenderer + ) { super({ id: LibraryListWidget.WIDGET_ID, label: LibraryListWidget.WIDGET_LABEL, @@ -25,7 +28,7 @@ export class LibraryListWidget extends ListWidget { installable: service, itemLabel: (item: LibraryPackage) => item.name, itemDeprecated: (item: LibraryPackage) => item.deprecated, - itemRenderer + itemRenderer, }); } @@ -33,17 +36,39 @@ export class LibraryListWidget extends ListWidget { protected init(): void { super.init(); this.toDispose.pushAll([ - this.notificationCenter.onLibraryInstalled(() => this.refresh(undefined)), - this.notificationCenter.onLibraryUninstalled(() => this.refresh(undefined)), + this.notificationCenter.onLibraryInstalled(() => + this.refresh(undefined) + ), + this.notificationCenter.onLibraryUninstalled(() => + this.refresh(undefined) + ), ]); } - protected async install({ item, progressId, version }: { item: LibraryPackage, progressId: string, version: Installable.Version }): Promise { - const dependencies = await this.service.listDependencies({ item, version, filterSelf: true }); + protected async install({ + item, + progressId, + version, + }: { + item: LibraryPackage; + progressId: string; + version: Installable.Version; + }): Promise { + const dependencies = await this.service.listDependencies({ + item, + version, + filterSelf: true, + }); let installDependencies: boolean | undefined = undefined; if (dependencies.length) { const message = document.createElement('div'); - message.innerHTML = `The library ${item.name}:${version} needs ${dependencies.length === 1 ? 'another dependency' : 'some other dependencies'} currently not installed:`; + message.innerHTML = `The library ${ + item.name + }:${version} needs ${ + dependencies.length === 1 + ? 'another dependency' + : 'some other dependencies' + } currently not installed:`; const listContainer = document.createElement('div'); listContainer.style.maxHeight = '300px'; listContainer.style.overflowY = 'auto'; @@ -58,24 +83,26 @@ export class LibraryListWidget extends ListWidget { listContainer.appendChild(list); message.appendChild(listContainer); const question = document.createElement('div'); - question.textContent = `Would you like to install ${dependencies.length === 1 ? 'the missing dependency' : 'all the missing dependencies'}?`; + question.textContent = `Would you like to install ${ + dependencies.length === 1 + ? 'the missing dependency' + : 'all the missing dependencies' + }?`; message.appendChild(question); const result = await new MessageBoxDialog({ title: `Dependencies for library ${item.name}:${version}`, message, - buttons: [ - 'Install all', - `Install ${item.name} only`, - 'Cancel' - ], - maxWidth: 740 // Aligned with `settings-dialog.css`. + buttons: ['Install all', `Install ${item.name} only`, 'Cancel'], + maxWidth: 740, // Aligned with `settings-dialog.css`. }).open(); if (result) { const { response } = result; - if (response === 0) { // All + if (response === 0) { + // All installDependencies = true; - } else if (response === 1) { // Current only + } else if (response === 1) { + // Current only installDependencies = false; } } @@ -85,33 +112,52 @@ export class LibraryListWidget extends ListWidget { } if (typeof installDependencies === 'boolean') { - await this.service.install({ item, version, progressId, installDependencies }); - this.messageService.info(`Successfully installed library ${item.name}:${version}`, { timeout: 3000 }); + await this.service.install({ + item, + version, + progressId, + installDependencies, + }); + this.messageService.info( + `Successfully installed library ${item.name}:${version}`, + { timeout: 3000 } + ); } } - protected async uninstall({ item, progressId }: { item: LibraryPackage, progressId: string }): Promise { + protected async uninstall({ + item, + progressId, + }: { + item: LibraryPackage; + progressId: string; + }): Promise { await super.uninstall({ item, progressId }); - this.messageService.info(`Successfully uninstalled library ${item.name}:${item.installedVersion}`, { timeout: 3000 }); + this.messageService.info( + `Successfully uninstalled library ${item.name}:${item.installedVersion}`, + { timeout: 3000 } + ); } - } class MessageBoxDialog extends AbstractDialog { - protected response: number; constructor(protected readonly options: MessageBoxDialog.Options) { super(options); - this.contentNode.appendChild(this.createMessageNode(this.options.message)); + this.contentNode.appendChild( + this.createMessageNode(this.options.message) + ); (options.buttons || ['OK']).forEach((text, index) => { const button = this.createButton(text); button.classList.add(index === 0 ? 'main' : 'secondary'); this.controlPanel.appendChild(button); - this.toDisposeOnDetach.push(addEventListener(button, 'click', () => { - this.response = index; - this.accept(); - })); + this.toDisposeOnDetach.push( + addEventListener(button, 'click', () => { + this.response = index; + this.accept(); + }) + ); }); } @@ -137,7 +183,6 @@ class MessageBoxDialog extends AbstractDialog { this.response = 0; super.handleEnter(event); } - } export namespace MessageBoxDialog { export interface Options extends DialogProps { diff --git a/arduino-ide-extension/src/browser/library/library-widget-frontend-contribution.ts b/arduino-ide-extension/src/browser/library/library-widget-frontend-contribution.ts index 5dbace9a..204d60bf 100644 --- a/arduino-ide-extension/src/browser/library/library-widget-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/library/library-widget-frontend-contribution.ts @@ -6,18 +6,20 @@ import { LibraryListWidget } from './library-list-widget'; import { ArduinoMenus } from '../menu/arduino-menus'; @injectable() -export class LibraryListWidgetFrontendContribution extends AbstractViewContribution implements FrontendApplicationContribution { - +export class LibraryListWidgetFrontendContribution + extends AbstractViewContribution + implements FrontendApplicationContribution +{ constructor() { super({ widgetId: LibraryListWidget.WIDGET_ID, widgetName: LibraryListWidget.WIDGET_LABEL, defaultWidgetOptions: { area: 'left', - rank: 3 + rank: 3, }, toggleCommandId: `${LibraryListWidget.WIDGET_ID}:toggle`, - toggleKeybinding: 'CtrlCmd+Shift+I' + toggleKeybinding: 'CtrlCmd+Shift+I', }); } @@ -30,9 +32,8 @@ export class LibraryListWidgetFrontendContribution extends AbstractViewContribut menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { commandId: this.toggleCommand.id, label: 'Manage Libraries...', - order: '3' + order: '3', }); } } - } diff --git a/arduino-ide-extension/src/browser/menu/arduino-menus.ts b/arduino-ide-extension/src/browser/menu/arduino-menus.ts index 02c829fe..4d52e7f9 100644 --- a/arduino-ide-extension/src/browser/menu/arduino-menus.ts +++ b/arduino-ide-extension/src/browser/menu/arduino-menus.ts @@ -1,41 +1,82 @@ import { isOSX } from '@theia/core/lib/common/os'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; -import { MAIN_MENU_BAR, MenuModelRegistry, MenuNode, MenuPath, SubMenuOptions } from '@theia/core/lib/common/menu'; +import { + MAIN_MENU_BAR, + MenuModelRegistry, + MenuNode, + MenuPath, + SubMenuOptions, +} from '@theia/core/lib/common/menu'; export namespace ArduinoMenus { - // Main menu // -- File export const FILE__SKETCH_GROUP = [...CommonMenus.FILE, '0_sketch']; export const FILE__PRINT_GROUP = [...CommonMenus.FILE, '1_print']; // XXX: on macOS, the "Preferences" and "Advanced" group is not under `File` // The empty path ensures no top level menu is created for the preferences, even if they contains sub menus - export const FILE__PREFERENCES_GROUP = [...(isOSX ? [''] : CommonMenus.FILE), '2_preferences']; - export const FILE__ADVANCED_GROUP = [...(isOSX ? [''] : CommonMenus.FILE), '3_advanced']; - export const FILE__ADVANCED_SUBMENU = [...FILE__ADVANCED_GROUP, '0_advanced_sub']; + export const FILE__PREFERENCES_GROUP = [ + ...(isOSX ? [''] : CommonMenus.FILE), + '2_preferences', + ]; + export const FILE__ADVANCED_GROUP = [ + ...(isOSX ? [''] : CommonMenus.FILE), + '3_advanced', + ]; + export const FILE__ADVANCED_SUBMENU = [ + ...FILE__ADVANCED_GROUP, + '0_advanced_sub', + ]; export const FILE__QUIT_GROUP = [...CommonMenus.FILE, '3_quit']; // -- File / Open Recent - export const FILE__OPEN_RECENT_SUBMENU = [...FILE__SKETCH_GROUP, '0_open_recent']; + export const FILE__OPEN_RECENT_SUBMENU = [ + ...FILE__SKETCH_GROUP, + '0_open_recent', + ]; // -- File / Sketchbook - export const FILE__SKETCHBOOK_SUBMENU = [...FILE__SKETCH_GROUP, '1_sketchbook']; + export const FILE__SKETCHBOOK_SUBMENU = [ + ...FILE__SKETCH_GROUP, + '1_sketchbook', + ]; // -- File / Examples export const FILE__EXAMPLES_SUBMENU = [...FILE__SKETCH_GROUP, '2_examples']; - export const EXAMPLES__BUILT_IN_GROUP = [...FILE__EXAMPLES_SUBMENU, '0_built_ins']; - export const EXAMPLES__ANY_BOARD_GROUP = [...FILE__EXAMPLES_SUBMENU, '1_any_board']; - export const EXAMPLES__CURRENT_BOARD_GROUP = [...FILE__EXAMPLES_SUBMENU, '2_current_board']; - export const EXAMPLES__USER_LIBS_GROUP = [...FILE__EXAMPLES_SUBMENU, '3_user_libs']; + export const EXAMPLES__BUILT_IN_GROUP = [ + ...FILE__EXAMPLES_SUBMENU, + '0_built_ins', + ]; + export const EXAMPLES__ANY_BOARD_GROUP = [ + ...FILE__EXAMPLES_SUBMENU, + '1_any_board', + ]; + export const EXAMPLES__CURRENT_BOARD_GROUP = [ + ...FILE__EXAMPLES_SUBMENU, + '2_current_board', + ]; + export const EXAMPLES__USER_LIBS_GROUP = [ + ...FILE__EXAMPLES_SUBMENU, + '3_user_libs', + ]; // -- Edit // `Copy`, `Copy to Forum`, `Paste`, etc. // Note: `1_undo` is the first group from Theia, we start with `2` - export const EDIT__TEXT_CONTROL_GROUP = [...CommonMenus.EDIT, '2_text_control']; + export const EDIT__TEXT_CONTROL_GROUP = [ + ...CommonMenus.EDIT, + '2_text_control', + ]; // `Comment/Uncomment`, etc. - export const EDIT__CODE_CONTROL_GROUP = [...CommonMenus.EDIT, '3_code_control']; - export const EDIT__FONT_CONTROL_GROUP = [...CommonMenus.EDIT, '4_font_control']; + export const EDIT__CODE_CONTROL_GROUP = [ + ...CommonMenus.EDIT, + '3_code_control', + ]; + export const EDIT__FONT_CONTROL_GROUP = [ + ...CommonMenus.EDIT, + '4_font_control', + ]; export const EDIT__FIND_GROUP = [...CommonMenus.EDIT, '5_find']; // -- Sketch @@ -62,35 +103,62 @@ export namespace ArduinoMenus { export const HELP__CONTROL_GROUP = [...CommonMenus.HELP, '2_control']; // `About` group // XXX: on macOS, the about group is not under `Help` - export const HELP__ABOUT_GROUP = [...(isOSX ? MAIN_MENU_BAR : CommonMenus.HELP), '999_about']; + export const HELP__ABOUT_GROUP = [ + ...(isOSX ? MAIN_MENU_BAR : CommonMenus.HELP), + '999_about', + ]; // ------------ // Context menus // -- Open export const OPEN_SKETCH__CONTEXT = ['arduino-open-sketch--context']; - export const OPEN_SKETCH__CONTEXT__OPEN_GROUP = [...OPEN_SKETCH__CONTEXT, '0_open']; - export const OPEN_SKETCH__CONTEXT__RECENT_GROUP = [...OPEN_SKETCH__CONTEXT, '1_recent']; - export const OPEN_SKETCH__CONTEXT__EXAMPLES_GROUP = [...OPEN_SKETCH__CONTEXT, '2_examples']; + export const OPEN_SKETCH__CONTEXT__OPEN_GROUP = [ + ...OPEN_SKETCH__CONTEXT, + '0_open', + ]; + export const OPEN_SKETCH__CONTEXT__RECENT_GROUP = [ + ...OPEN_SKETCH__CONTEXT, + '1_recent', + ]; + export const OPEN_SKETCH__CONTEXT__EXAMPLES_GROUP = [ + ...OPEN_SKETCH__CONTEXT, + '2_examples', + ]; // -- Sketch control export const SKETCH_CONTROL__CONTEXT = ['arduino-sketch-control--context']; // `New Tab`, `Rename`, `Delete` - export const SKETCH_CONTROL__CONTEXT__MAIN_GROUP = [...SKETCH_CONTROL__CONTEXT, '0_main']; + export const SKETCH_CONTROL__CONTEXT__MAIN_GROUP = [ + ...SKETCH_CONTROL__CONTEXT, + '0_main', + ]; // `Previous Tab`, `Next Tab` - export const SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP = [...SKETCH_CONTROL__CONTEXT, '1_navigation']; + export const SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP = [ + ...SKETCH_CONTROL__CONTEXT, + '1_navigation', + ]; // Sketch files opened in editors - export const SKETCH_CONTROL__CONTEXT__RESOURCES_GROUP = [...SKETCH_CONTROL__CONTEXT, '2_resources']; - + export const SKETCH_CONTROL__CONTEXT__RESOURCES_GROUP = [ + ...SKETCH_CONTROL__CONTEXT, + '2_resources', + ]; } /** * This is a hack. It removes a submenu with all its children if any. * Theia cannot dispose submenu entries with a proper API: https://github.com/eclipse-theia/theia/issues/7299 */ -export function unregisterSubmenu(menuPath: string[], menuRegistry: MenuModelRegistry): void { +export function unregisterSubmenu( + menuPath: string[], + menuRegistry: MenuModelRegistry +): void { if (menuPath.length < 2) { - throw new Error(`Expected at least two item as a menu-path. Got ${JSON.stringify(menuPath)} instead.`); + throw new Error( + `Expected at least two item as a menu-path. Got ${JSON.stringify( + menuPath + )} instead.` + ); } const toRemove = menuPath[menuPath.length - 1]; const parentMenuPath = menuPath.slice(0, menuPath.length - 1); @@ -99,7 +167,9 @@ export function unregisterSubmenu(menuPath: string[], menuRegistry: MenuModelReg const parent = menuRegistry.getMenu(parentMenuPath); const index = parent.children.findIndex(({ id }) => id === toRemove); if (index === -1) { - throw new Error(`Could not find menu with menu-path: ${JSON.stringify(menuPath)}.`); + throw new Error( + `Could not find menu with menu-path: ${JSON.stringify(menuPath)}.` + ); } (parent.children as Array).splice(index, 1); } @@ -108,8 +178,11 @@ export function unregisterSubmenu(menuPath: string[], menuRegistry: MenuModelReg * Special menu node that is not backed by any commands and is always disabled. */ export class PlaceholderMenuNode implements MenuNode { - - constructor(protected readonly menuPath: MenuPath, readonly label: string, protected options: SubMenuOptions = { order: '0' }) { } + constructor( + protected readonly menuPath: MenuPath, + readonly label: string, + protected options: SubMenuOptions = { order: '0' } + ) {} get icon(): string | undefined { return this.options?.iconClass; @@ -122,5 +195,4 @@ export class PlaceholderMenuNode implements MenuNode { get id(): string { return [...this.menuPath, 'placeholder'].join('-'); } - } diff --git a/arduino-ide-extension/src/browser/monitor/monitor-connection.ts b/arduino-ide-extension/src/browser/monitor/monitor-connection.ts index df52954c..28f93efe 100644 --- a/arduino-ide-extension/src/browser/monitor/monitor-connection.ts +++ b/arduino-ide-extension/src/browser/monitor/monitor-connection.ts @@ -3,9 +3,19 @@ import { deepClone } from '@theia/core/lib/common/objects'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { MessageService } from '@theia/core/lib/common/message-service'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; -import { MonitorService, MonitorConfig, MonitorError, Status } from '../../common/protocol/monitor-service'; +import { + MonitorService, + MonitorConfig, + MonitorError, + Status, +} from '../../common/protocol/monitor-service'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { Port, Board, BoardsService, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service'; +import { + Port, + Board, + BoardsService, + AttachedBoardsChangeEvent, +} from '../../common/protocol/boards-service'; import { MonitorServiceClientImpl } from './monitor-service-client-impl'; import { BoardsConfig } from '../boards/boards-config'; import { MonitorModel } from './monitor-model'; @@ -13,7 +23,6 @@ import { NotificationCenter } from '../notification-center'; @injectable() export class MonitorConnection { - @inject(MonitorModel) protected readonly monitorModel: MonitorModel; @@ -43,8 +52,10 @@ export class MonitorConnection { * Note: The idea is to toggle this property from the UI (`Monitor` view) * and the boards config and the boards attachment/detachment logic can be at on place, here. */ - protected _autoConnect: boolean = false; - protected readonly onConnectionChangedEmitter = new Emitter(); + protected _autoConnect = false; + protected readonly onConnectionChangedEmitter = new Emitter< + MonitorConnection.State | undefined + >(); /** * This emitter forwards all read events **iff** the connection is established. */ @@ -60,7 +71,7 @@ export class MonitorConnection { @postConstruct() protected init(): void { - this.monitorServiceClient.onError(async error => { + this.monitorServiceClient.onError(async (error) => { let shouldReconnect = false; if (this.state) { const { code, config } = error; @@ -68,21 +79,40 @@ export class MonitorConnection { const options = { timeout: 3000 }; switch (code) { case MonitorError.ErrorCodes.CLIENT_CANCEL: { - console.debug(`Connection was canceled by client: ${MonitorConnection.State.toString(this.state)}.`); + console.debug( + `Connection was canceled by client: ${MonitorConnection.State.toString( + this.state + )}.` + ); break; } case MonitorError.ErrorCodes.DEVICE_BUSY: { - this.messageService.warn(`Connection failed. Serial port is busy: ${Port.toString(port)}.`, options); + this.messageService.warn( + `Connection failed. Serial port is busy: ${Port.toString( + port + )}.`, + options + ); shouldReconnect = this.autoConnect; this.monitorErrors.push(error); break; } case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: { - this.messageService.info(`Disconnected ${Board.toString(board, { useFqbn: false })} from ${Port.toString(port)}.`, options); + this.messageService.info( + `Disconnected ${Board.toString(board, { + useFqbn: false, + })} from ${Port.toString(port)}.`, + options + ); break; } case undefined: { - this.messageService.error(`Unexpected error. Reconnecting ${Board.toString(board)} on port ${Port.toString(port)}.`, options); + this.messageService.error( + `Unexpected error. Reconnecting ${Board.toString( + board + )} on port ${Port.toString(port)}.`, + options + ); console.error(JSON.stringify(error)); shouldReconnect = this.connected && this.autoConnect; break; @@ -93,32 +123,62 @@ export class MonitorConnection { this.onConnectionChangedEmitter.fire(this.state); if (shouldReconnect) { if (this.monitorErrors.length >= 10) { - this.messageService.warn(`Failed to reconnect ${Board.toString(board, { useFqbn: false })} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(port)} serial port is busy. after 10 consecutive attempts.`); + this.messageService.warn( + `Failed to reconnect ${Board.toString(board, { + useFqbn: false, + })} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString( + port + )} serial port is busy. after 10 consecutive attempts.` + ); this.monitorErrors.length = 0; } else { - const attempts = (this.monitorErrors.length || 1); + const attempts = this.monitorErrors.length || 1; if (this.reconnectTimeout !== undefined) { // Clear the previous timer. window.clearTimeout(this.reconnectTimeout); } const timeout = attempts * 1000; - this.messageService.warn(`Reconnecting ${Board.toString(board, { useFqbn: false })} to ${Port.toString(port)} in ${attempts} seconds...`, { timeout }); - this.reconnectTimeout = window.setTimeout(() => this.connect(oldState.config), timeout); + this.messageService.warn( + `Reconnecting ${Board.toString(board, { + useFqbn: false, + })} to ${Port.toString( + port + )} in ${attempts} seconds...`, + { timeout } + ); + this.reconnectTimeout = window.setTimeout( + () => this.connect(oldState.config), + timeout + ); } } } }); - this.boardsServiceProvider.onBoardsConfigChanged(this.handleBoardConfigChange.bind(this)); - this.notificationCenter.onAttachedBoardsChanged(event => { + this.boardsServiceProvider.onBoardsConfigChanged( + this.handleBoardConfigChange.bind(this) + ); + this.notificationCenter.onAttachedBoardsChanged((event) => { if (this.autoConnect && this.connected) { const { boardsConfig } = this.boardsServiceProvider; - if (this.boardsServiceProvider.canUploadTo(boardsConfig, { silent: false })) { + if ( + this.boardsServiceProvider.canUploadTo(boardsConfig, { + silent: false, + }) + ) { const { attached } = AttachedBoardsChangeEvent.diff(event); - if (attached.boards.some(board => !!board.port && BoardsConfig.Config.sameAs(boardsConfig, board))) { - const { selectedBoard: board, selectedPort: port } = boardsConfig; + if ( + attached.boards.some( + (board) => + !!board.port && + BoardsConfig.Config.sameAs(boardsConfig, board) + ) + ) { + const { selectedBoard: board, selectedPort: port } = + boardsConfig; const { baudRate } = this.monitorModel; - this.disconnect() - .then(() => this.connect({ board, port, baudRate })); + this.disconnect().then(() => + this.connect({ board, port, baudRate }) + ); } } } @@ -151,10 +211,12 @@ export class MonitorConnection { if (!oldValue && value) { // We have to make sure the previous boards config has been restored. // Otherwise, we might start the auto-connection without configured boards. - this.applicationState.reachedState('started_contributions').then(() => { - const { boardsConfig } = this.boardsServiceProvider; - this.handleBoardConfigChange(boardsConfig); - }); + this.applicationState + .reachedState('started_contributions') + .then(() => { + const { boardsConfig } = this.boardsServiceProvider; + this.handleBoardConfigChange(boardsConfig); + }); } else if (oldValue && !value) { if (this.reconnectTimeout !== undefined) { window.clearTimeout(this.reconnectTimeout); @@ -170,7 +232,11 @@ export class MonitorConnection { return disconnectStatus; } } - console.info(`>>> Creating serial monitor connection for ${Board.toString(config.board)} on port ${Port.toString(config.port)}...`); + console.info( + `>>> Creating serial monitor connection for ${Board.toString( + config.board + )} on port ${Port.toString(config.port)}...` + ); const connectStatus = await this.monitorService.connect(config); if (Status.isOK(connectStatus)) { const requestMessage = () => { @@ -180,10 +246,15 @@ export class MonitorConnection { requestMessage(); } }); - } + }; requestMessage(); this.state = { config }; - console.info(`<<< Serial monitor connection created for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`); + console.info( + `<<< Serial monitor connection created for ${Board.toString( + config.board, + { useFqbn: false } + )} on port ${Port.toString(config.port)}.` + ); } this.onConnectionChangedEmitter.fire(this.state); return Status.isOK(connectStatus); @@ -200,9 +271,17 @@ export class MonitorConnection { console.log('>>> Disposing existing monitor connection...'); const status = await this.monitorService.disconnect(); if (Status.isOK(status)) { - console.log(`<<< Disposed connection. Was: ${MonitorConnection.State.toString(stateCopy)}`); + console.log( + `<<< Disposed connection. Was: ${MonitorConnection.State.toString( + stateCopy + )}` + ); } else { - console.warn(`<<< Could not dispose connection. Activate connection: ${MonitorConnection.State.toString(stateCopy)}`); + console.warn( + `<<< Could not dispose connection. Activate connection: ${MonitorConnection.State.toString( + stateCopy + )}` + ); } this.state = undefined; this.onConnectionChangedEmitter.fire(this.state); @@ -218,8 +297,9 @@ export class MonitorConnection { if (!this.connected) { return Status.NOT_CONNECTED; } - return new Promise(resolve => { - this.monitorService.send(data + this.monitorModel.lineEnding) + return new Promise((resolve) => { + this.monitorService + .send(data + this.monitorModel.lineEnding) .then(() => resolve(Status.OK)); }); } @@ -232,14 +312,24 @@ export class MonitorConnection { return this.onReadEmitter.event; } - protected async handleBoardConfigChange(boardsConfig: BoardsConfig.Config): Promise { + protected async handleBoardConfigChange( + boardsConfig: BoardsConfig.Config + ): Promise { if (this.autoConnect) { - if (this.boardsServiceProvider.canUploadTo(boardsConfig, { silent: false })) { + if ( + this.boardsServiceProvider.canUploadTo(boardsConfig, { + silent: false, + }) + ) { // Instead of calling `getAttachedBoards` and filtering for `AttachedSerialBoard` we have to check the available ports. // The connected board might be unknown. See: https://github.com/arduino/arduino-pro-ide/issues/127#issuecomment-563251881 - this.boardsService.getAvailablePorts().then(ports => { - if (ports.some(port => Port.equals(port, boardsConfig.selectedPort))) { - new Promise(resolve => { + this.boardsService.getAvailablePorts().then((ports) => { + if ( + ports.some((port) => + Port.equals(port, boardsConfig.selectedPort) + ) + ) { + new Promise((resolve) => { // First, disconnect if connected. if (this.connected) { this.disconnect().then(() => resolve()); @@ -248,7 +338,8 @@ export class MonitorConnection { resolve(); }).then(() => { // Then (re-)connect. - const { selectedBoard: board, selectedPort: port } = boardsConfig; + const { selectedBoard: board, selectedPort: port } = + boardsConfig; const { baudRate } = this.monitorModel; this.connect({ board, port, baudRate }); }); @@ -257,11 +348,9 @@ export class MonitorConnection { } } } - } export namespace MonitorConnection { - export interface State { readonly config: MonitorConfig; } @@ -273,5 +362,4 @@ export namespace MonitorConnection { return `${Board.toString(board)} ${Port.toString(port)}`; } } - } diff --git a/arduino-ide-extension/src/browser/monitor/monitor-model.ts b/arduino-ide-extension/src/browser/monitor/monitor-model.ts index d42b5ec6..9b448341 100644 --- a/arduino-ide-extension/src/browser/monitor/monitor-model.ts +++ b/arduino-ide-extension/src/browser/monitor/monitor-model.ts @@ -1,12 +1,14 @@ import { injectable, inject } from 'inversify'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { MonitorConfig } from '../../common/protocol/monitor-service'; -import { FrontendApplicationContribution, LocalStorageService } from '@theia/core/lib/browser'; +import { + FrontendApplicationContribution, + LocalStorageService, +} from '@theia/core/lib/browser'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; @injectable() export class MonitorModel implements FrontendApplicationContribution { - protected static STORAGE_ID = 'arduino-monitor-model'; @inject(LocalStorageService) @@ -15,7 +17,9 @@ export class MonitorModel implements FrontendApplicationContribution { @inject(BoardsServiceProvider) protected readonly boardsServiceClient: BoardsServiceProvider; - protected readonly onChangeEmitter: Emitter>; + protected readonly onChangeEmitter: Emitter< + MonitorModel.State.Change + >; protected _autoscroll: boolean; protected _timestamp: boolean; protected _baudRate: MonitorConfig.BaudRate; @@ -26,15 +30,19 @@ export class MonitorModel implements FrontendApplicationContribution { this._timestamp = false; this._baudRate = MonitorConfig.BaudRate.DEFAULT; this._lineEnding = MonitorModel.EOL.DEFAULT; - this.onChangeEmitter = new Emitter>(); + this.onChangeEmitter = new Emitter< + MonitorModel.State.Change + >(); } onStart(): void { - this.localStorageService.getData(MonitorModel.STORAGE_ID).then(state => { - if (state) { - this.restoreState(state); - } - }); + this.localStorageService + .getData(MonitorModel.STORAGE_ID) + .then((state) => { + if (state) { + this.restoreState(state); + } + }); } get onChange(): Event> { @@ -48,7 +56,12 @@ export class MonitorModel implements FrontendApplicationContribution { toggleAutoscroll(): void { this._autoscroll = !this._autoscroll; this.storeState(); - this.storeState().then(() => this.onChangeEmitter.fire({ property: 'autoscroll', value: this._autoscroll })); + this.storeState().then(() => + this.onChangeEmitter.fire({ + property: 'autoscroll', + value: this._autoscroll, + }) + ); } get timestamp(): boolean { @@ -57,7 +70,12 @@ export class MonitorModel implements FrontendApplicationContribution { toggleTimestamp(): void { this._timestamp = !this._timestamp; - this.storeState().then(() => this.onChangeEmitter.fire({ property: 'timestamp', value: this._timestamp })); + this.storeState().then(() => + this.onChangeEmitter.fire({ + property: 'timestamp', + value: this._timestamp, + }) + ); } get baudRate(): MonitorConfig.BaudRate { @@ -66,7 +84,12 @@ export class MonitorModel implements FrontendApplicationContribution { set baudRate(baudRate: MonitorConfig.BaudRate) { this._baudRate = baudRate; - this.storeState().then(() => this.onChangeEmitter.fire({ property: 'baudRate', value: this._baudRate })); + this.storeState().then(() => + this.onChangeEmitter.fire({ + property: 'baudRate', + value: this._baudRate, + }) + ); } get lineEnding(): MonitorModel.EOL { @@ -75,7 +98,12 @@ export class MonitorModel implements FrontendApplicationContribution { set lineEnding(lineEnding: MonitorModel.EOL) { this._lineEnding = lineEnding; - this.storeState().then(() => this.onChangeEmitter.fire({ property: 'lineEnding', value: this._lineEnding })); + this.storeState().then(() => + this.onChangeEmitter.fire({ + property: 'lineEnding', + value: this._lineEnding, + }) + ); } protected restoreState(state: MonitorModel.State): void { @@ -90,14 +118,12 @@ export class MonitorModel implements FrontendApplicationContribution { autoscroll: this._autoscroll, timestamp: this._timestamp, baudRate: this._baudRate, - lineEnding: this._lineEnding + lineEnding: this._lineEnding, }); } - } export namespace MonitorModel { - export interface State { autoscroll: boolean; timestamp: boolean; @@ -115,5 +141,4 @@ export namespace MonitorModel { export namespace EOL { export const DEFAULT: EOL = '\n'; } - } diff --git a/arduino-ide-extension/src/browser/monitor/monitor-service-client-impl.ts b/arduino-ide-extension/src/browser/monitor/monitor-service-client-impl.ts index 1e8ea29e..eb3849c9 100644 --- a/arduino-ide-extension/src/browser/monitor/monitor-service-client-impl.ts +++ b/arduino-ide-extension/src/browser/monitor/monitor-service-client-impl.ts @@ -1,15 +1,16 @@ import { injectable } from 'inversify'; import { Emitter } from '@theia/core/lib/common/event'; -import { MonitorServiceClient, MonitorError } from '../../common/protocol/monitor-service'; +import { + MonitorServiceClient, + MonitorError, +} from '../../common/protocol/monitor-service'; @injectable() export class MonitorServiceClientImpl implements MonitorServiceClient { - protected readonly onErrorEmitter = new Emitter(); readonly onError = this.onErrorEmitter.event; notifyError(error: MonitorError): void { this.onErrorEmitter.fire(error); } - } diff --git a/arduino-ide-extension/src/browser/monitor/monitor-view-contribution.tsx b/arduino-ide-extension/src/browser/monitor/monitor-view-contribution.tsx index 3a4c42b0..bff77318 100644 --- a/arduino-ide-extension/src/browser/monitor/monitor-view-contribution.tsx +++ b/arduino-ide-extension/src/browser/monitor/monitor-view-contribution.tsx @@ -3,7 +3,10 @@ import { injectable, inject } from 'inversify'; import { AbstractViewContribution } from '@theia/core/lib/browser'; import { MonitorWidget } from './monitor-widget'; import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core'; -import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { + TabBarToolbarContribution, + TabBarToolbarRegistry, +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { MonitorModel } from './monitor-model'; import { ArduinoMenus } from '../menu/arduino-menus'; @@ -12,25 +15,28 @@ export namespace SerialMonitor { export namespace Commands { export const AUTOSCROLL: Command = { id: 'serial-monitor-autoscroll', - label: 'Autoscroll' - } + label: 'Autoscroll', + }; export const TIMESTAMP: Command = { id: 'serial-monitor-timestamp', - label: 'Timestamp' - } + label: 'Timestamp', + }; export const CLEAR_OUTPUT: Command = { id: 'serial-monitor-clear-output', label: 'Clear Output', - iconClass: 'clear-all' - } + iconClass: 'clear-all', + }; } } @injectable() -export class MonitorViewContribution extends AbstractViewContribution implements TabBarToolbarContribution { - +export class MonitorViewContribution + extends AbstractViewContribution + implements TabBarToolbarContribution +{ static readonly TOGGLE_SERIAL_MONITOR = MonitorWidget.ID + ':toggle'; - static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR = MonitorWidget.ID + ':toggle-toolbar'; + static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR = + MonitorWidget.ID + ':toggle-toolbar'; @inject(MonitorModel) protected readonly model: MonitorModel; @@ -39,11 +45,11 @@ export class MonitorViewContribution extends AbstractViewContribution this.renderAutoScrollButton(), - isVisible: widget => widget instanceof MonitorWidget, - onDidChange: this.model.onChange as any // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/ + isVisible: (widget) => widget instanceof MonitorWidget, + onDidChange: this.model.onChange as any, // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/ }); registry.registerItem({ id: 'monitor-timestamp', render: () => this.renderTimestampButton(), - isVisible: widget => widget instanceof MonitorWidget, - onDidChange: this.model.onChange as any // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/ + isVisible: (widget) => widget instanceof MonitorWidget, + onDidChange: this.model.onChange as any, // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/ }); registry.registerItem({ id: SerialMonitor.Commands.CLEAR_OUTPUT.id, command: SerialMonitor.Commands.CLEAR_OUTPUT.id, - tooltip: 'Clear Output' + tooltip: 'Clear Output', }); } registerCommands(commands: CommandRegistry): void { commands.registerCommand(SerialMonitor.Commands.CLEAR_OUTPUT, { - isEnabled: widget => widget instanceof MonitorWidget, - isVisible: widget => widget instanceof MonitorWidget, - execute: widget => { + isEnabled: (widget) => widget instanceof MonitorWidget, + isVisible: (widget) => widget instanceof MonitorWidget, + execute: (widget) => { if (widget instanceof MonitorWidget) { widget.clearConsole(); } - } + }, }); if (this.toggleCommand) { - commands.registerCommand(this.toggleCommand, { execute: () => this.toggle() }); - commands.registerCommand({ id: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR }, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right', - execute: () => this.toggle() + commands.registerCommand(this.toggleCommand, { + execute: () => this.toggle(), }); + commands.registerCommand( + { id: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR }, + { + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'right', + execute: () => this.toggle(), + } + ); } } @@ -105,13 +117,17 @@ export class MonitorViewContribution extends AbstractViewContribution -
- ; + return ( + +
+
+ ); } protected readonly toggleAutoScroll = () => this.doToggleAutoScroll(); @@ -120,18 +136,21 @@ export class MonitorViewContribution extends AbstractViewContribution -
- ; + return ( + +
+
+ ); } protected readonly toggleTimestamp = () => this.doToggleTimestamp(); protected async doToggleTimestamp(): Promise { this.model.toggleTimestamp(); } - } diff --git a/arduino-ide-extension/src/browser/monitor/monitor-widget.tsx b/arduino-ide-extension/src/browser/monitor/monitor-widget.tsx index 993c0efb..4f9edc98 100644 --- a/arduino-ide-extension/src/browser/monitor/monitor-widget.tsx +++ b/arduino-ide-extension/src/browser/monitor/monitor-widget.tsx @@ -5,8 +5,16 @@ import { OptionsType } from 'react-select/src/types'; import { isOSX } from '@theia/core/lib/common/os'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { Key, KeyCode } from '@theia/core/lib/browser/keys'; -import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable' -import { ReactWidget, Message, Widget, MessageLoop } from '@theia/core/lib/browser/widgets'; +import { + DisposableCollection, + Disposable, +} from '@theia/core/lib/common/disposable'; +import { + ReactWidget, + Message, + Widget, + MessageLoop, +} from '@theia/core/lib/browser/widgets'; import { Board, Port } from '../../common/protocol/boards-service'; import { MonitorConfig } from '../../common/protocol/monitor-service'; import { ArduinoSelect } from '../widgets/arduino-select'; @@ -16,7 +24,6 @@ import { MonitorServiceClientImpl } from './monitor-service-client-impl'; @injectable() export class MonitorWidget extends ReactWidget { - static readonly ID = 'serial-monitor'; @inject(MonitorModel) @@ -49,18 +56,24 @@ export class MonitorWidget extends ReactWidget { this.title.closable = true; this.scrollOptions = undefined; this.toDispose.push(this.clearOutputEmitter); - this.toDispose.push(Disposable.create(() => { - this.monitorConnection.autoConnect = false; - if (this.monitorConnection.connected) { - this.monitorConnection.disconnect(); - } - })); + this.toDispose.push( + Disposable.create(() => { + this.monitorConnection.autoConnect = false; + if (this.monitorConnection.connected) { + this.monitorConnection.disconnect(); + } + }) + ); } @postConstruct() protected init(): void { this.update(); - this.toDispose.push(this.monitorConnection.onConnectionChanged(() => this.clearConsole())); + this.toDispose.push( + this.monitorConnection.onConnectionChanged(() => + this.clearConsole() + ) + ); } clearConsole(): void { @@ -106,72 +119,93 @@ export class MonitorWidget extends ReactWidget { return; } this.focusNode = element; - requestAnimationFrame(() => MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest)); - } + requestAnimationFrame(() => + MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest) + ); + }; protected get lineEndings(): OptionsType> { return [ { label: 'No Line Ending', - value: '' + value: '', }, { label: 'New Line', - value: '\n' + value: '\n', }, { label: 'Carriage Return', - value: '\r' + value: '\r', }, { label: 'Both NL & CR', - value: '\r\n' - } + value: '\r\n', + }, ]; } - protected get baudRates(): OptionsType> { - const baudRates: Array = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]; - return baudRates.map(baudRate => ({ label: baudRate + ' baud', value: baudRate })); + protected get baudRates(): OptionsType< + SelectOption + > { + const baudRates: Array = [ + 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, + ]; + return baudRates.map((baudRate) => ({ + label: baudRate + ' baud', + value: baudRate, + })); } protected render(): React.ReactNode { const { baudRates, lineEndings } = this; - const lineEnding = lineEndings.find(item => item.value === this.monitorModel.lineEnding) || lineEndings[1]; // Defaults to `\n`. - const baudRate = baudRates.find(item => item.value === this.monitorModel.baudRate) || baudRates[4]; // Defaults to `9600`. - return
-
-
- -
-
-
- + const lineEnding = + lineEndings.find( + (item) => item.value === this.monitorModel.lineEnding + ) || lineEndings[1]; // Defaults to `\n`. + const baudRate = + baudRates.find( + (item) => item.value === this.monitorModel.baudRate + ) || baudRates[4]; // Defaults to `9600`. + return ( +
+
+
+
-
- +
+
+ +
+
+ +
+
+ +
-
- -
-
; + ); } protected readonly onSend = (value: string) => this.doSend(value); @@ -179,14 +213,17 @@ export class MonitorWidget extends ReactWidget { this.monitorConnection.send(value); } - protected readonly onChangeLineEnding = (option: SelectOption) => { + protected readonly onChangeLineEnding = ( + option: SelectOption + ) => { this.monitorModel.lineEnding = option.value; - } + }; - protected readonly onChangeBaudRate = (option: SelectOption) => { + protected readonly onChangeBaudRate = ( + option: SelectOption + ) => { this.monitorModel.baudRate = option.value; - } - + }; } export namespace SerialMonitorSendInput { @@ -200,8 +237,10 @@ export namespace SerialMonitorSendInput { } } -export class SerialMonitorSendInput extends React.Component { - +export class SerialMonitorSendInput extends React.Component< + SerialMonitorSendInput.Props, + SerialMonitorSendInput.State +> { constructor(props: Readonly) { super(props); this.state = { text: '' }; @@ -211,30 +250,39 @@ export class SerialMonitorSendInput extends React.Component + return ( + + ); } protected get placeholder(): string { const { monitorConfig } = this.props; if (!monitorConfig) { - return 'Not connected. Select a board and a port to connect automatically.' + return 'Not connected. Select a board and a port to connect automatically.'; } const { board, port } = monitorConfig; - return `Message (${isOSX ? '⌘' : 'Ctrl'}+Enter to send message to '${Board.toString(board, { useFqbn: false })}' on '${Port.toString(port)}')`; + return `Message (${ + isOSX ? '⌘' : 'Ctrl' + }+Enter to send message to '${Board.toString(board, { + useFqbn: false, + })}' on '${Port.toString(port)}')`; } protected setRef = (element: HTMLElement | null) => { if (this.props.resolveFocus) { this.props.resolveFocus(element || undefined); } - } + }; protected onChange(event: React.ChangeEvent): void { this.setState({ text: event.target.value }); @@ -254,7 +302,6 @@ export class SerialMonitorSendInput extends React.Component { - +export class SerialMonitorOutput extends React.Component< + SerialMonitorOutput.Props, + SerialMonitorOutput.State +> { /** * Do not touch it. It is used to be able to "follow" the serial monitor log. */ @@ -279,16 +328,26 @@ export class SerialMonitorOutput extends React.Component) { super(props); - this.state = { content: '', timestamp: this.props.monitorModel.timestamp }; + this.state = { + content: '', + timestamp: this.props.monitorModel.timestamp, + }; } render(): React.ReactNode { - return -
- {this.state.content} -
-
{ this.anchor = element; }} /> - ; + return ( + +
+ {this.state.content} +
+
{ + this.anchor = element; + }} + /> + + ); } componentDidMount(): void { @@ -296,8 +355,11 @@ export class SerialMonitorOutput extends React.Component { const rawLines = message.split('\n'); - const lines: string[] = [] - const timestamp = () => this.state.timestamp ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` : ''; + const lines: string[] = []; + const timestamp = () => + this.state.timestamp + ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` + : ''; for (let i = 0; i < rawLines.length; i++) { if (i === 0 && this.state.content.length !== 0) { lines.push(rawLines[i]); @@ -314,7 +376,7 @@ export class SerialMonitorOutput extends React.Component { diff --git a/arduino-ide-extension/src/browser/notification-center.ts b/arduino-ide-extension/src/browser/notification-center.ts index 4dbd64ba..03b85095 100644 --- a/arduino-ide-extension/src/browser/notification-center.ts +++ b/arduino-ide-extension/src/browser/notification-center.ts @@ -3,25 +3,48 @@ import { Emitter } from '@theia/core/lib/common/event'; import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; -import { NotificationServiceClient, NotificationServiceServer } from '../common/protocol/notification-service'; -import { AttachedBoardsChangeEvent, BoardsPackage, LibraryPackage, Config, Sketch } from '../common/protocol'; +import { + NotificationServiceClient, + NotificationServiceServer, +} from '../common/protocol/notification-service'; +import { + AttachedBoardsChangeEvent, + BoardsPackage, + LibraryPackage, + Config, + Sketch, +} from '../common/protocol'; @injectable() -export class NotificationCenter implements NotificationServiceClient, FrontendApplicationContribution { - +export class NotificationCenter + implements NotificationServiceClient, FrontendApplicationContribution +{ @inject(NotificationServiceServer) protected readonly server: JsonRpcProxy; protected readonly indexUpdatedEmitter = new Emitter(); protected readonly daemonStartedEmitter = new Emitter(); protected readonly daemonStoppedEmitter = new Emitter(); - protected readonly configChangedEmitter = new Emitter<{ config: Config | undefined }>(); - protected readonly platformInstalledEmitter = new Emitter<{ item: BoardsPackage }>(); - protected readonly platformUninstalledEmitter = new Emitter<{ item: BoardsPackage }>(); - protected readonly libraryInstalledEmitter = new Emitter<{ item: LibraryPackage }>(); - protected readonly libraryUninstalledEmitter = new Emitter<{ item: LibraryPackage }>(); - protected readonly attachedBoardsChangedEmitter = new Emitter(); - protected readonly recentSketchesChangedEmitter = new Emitter<{ sketches: Sketch[] }>(); + protected readonly configChangedEmitter = new Emitter<{ + config: Config | undefined; + }>(); + protected readonly platformInstalledEmitter = new Emitter<{ + item: BoardsPackage; + }>(); + protected readonly platformUninstalledEmitter = new Emitter<{ + item: BoardsPackage; + }>(); + protected readonly libraryInstalledEmitter = new Emitter<{ + item: LibraryPackage; + }>(); + protected readonly libraryUninstalledEmitter = new Emitter<{ + item: LibraryPackage; + }>(); + protected readonly attachedBoardsChangedEmitter = + new Emitter(); + protected readonly recentSketchesChangedEmitter = new Emitter<{ + sketches: Sketch[]; + }>(); protected readonly toDispose = new DisposableCollection( this.indexUpdatedEmitter, @@ -94,5 +117,4 @@ export class NotificationCenter implements NotificationServiceClient, FrontendAp notifyRecentSketchesChanged(event: { sketches: Sketch[] }): void { this.recentSketchesChangedEmitter.fire(event); } - } diff --git a/arduino-ide-extension/src/browser/response-service-impl.ts b/arduino-ide-extension/src/browser/response-service-impl.ts index d1fc6ce6..e99f6474 100644 --- a/arduino-ide-extension/src/browser/response-service-impl.ts +++ b/arduino-ide-extension/src/browser/response-service-impl.ts @@ -2,18 +2,22 @@ import { inject, injectable } from 'inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { OutputContribution } from '@theia/output/lib/browser/output-contribution'; import { OutputChannelManager } from '@theia/output/lib/common/output-channel'; -import { ResponseService, OutputMessage, ProgressMessage } from '../common/protocol/response-service'; +import { + ResponseService, + OutputMessage, + ProgressMessage, +} from '../common/protocol/response-service'; @injectable() export class ResponseServiceImpl implements ResponseService { - @inject(OutputContribution) protected outputContribution: OutputContribution; @inject(OutputChannelManager) protected outputChannelManager: OutputChannelManager; - protected readonly progressDidChangeEmitter = new Emitter(); + protected readonly progressDidChangeEmitter = + new Emitter(); readonly onProgressDidChange = this.progressDidChangeEmitter.event; appendToOutput(message: OutputMessage): void { @@ -30,5 +34,4 @@ export class ResponseServiceImpl implements ResponseService { reportProgress(progress: ProgressMessage): void { this.progressDidChangeEmitter.fire(progress); } - } diff --git a/arduino-ide-extension/src/browser/settings.tsx b/arduino-ide-extension/src/browser/settings.tsx index da957056..5242a4de 100644 --- a/arduino-ide-extension/src/browser/settings.tsx +++ b/arduino-ide-extension/src/browser/settings.tsx @@ -16,9 +16,23 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; -import { AbstractDialog, DialogProps, PreferenceService, PreferenceScope, DialogError, ReactWidget } from '@theia/core/lib/browser'; +import { + AbstractDialog, + DialogProps, + PreferenceService, + PreferenceScope, + DialogError, + ReactWidget, +} from '@theia/core/lib/browser'; import { Index } from '../common/types'; -import { CompilerWarnings, CompilerWarningLiterals, ConfigService, FileSystemExt, Network, ProxySettings } from '../common/protocol'; +import { + CompilerWarnings, + CompilerWarningLiterals, + ConfigService, + FileSystemExt, + Network, + ProxySettings, +} from '../common/protocol'; export interface Settings extends Index { editorFontSize: number; // `editor.fontSize` @@ -40,16 +54,13 @@ export interface Settings extends Index { network: Network; // CLI } export namespace Settings { - export function belongsToCli(key: K): boolean { return key === 'sketchbookPath' || key === 'additionalUrls'; } - } @injectable() export class SettingsService { - @inject(FileService) protected readonly fileService: FileService; @@ -94,28 +105,42 @@ export class SettingsService { verboseOnUpload, verifyAfterUpload, enableLsLogs, - cliConfig + cliConfig, ] = await Promise.all([ this.preferenceService.get('editor.fontSize', 12), - this.preferenceService.get('workbench.colorTheme', 'arduino-theme'), + this.preferenceService.get( + 'workbench.colorTheme', + 'arduino-theme' + ), this.preferenceService.get<'on' | 'off'>('editor.autoSave', 'on'), - this.preferenceService.get('editor.quickSuggestion', { - 'other': false, - 'comments': false, - 'strings': false - }), - this.preferenceService.get('arduino.window.autoScale', true), + this.preferenceService.get>( + 'editor.quickSuggestion', + { + other: false, + comments: false, + strings: false, + } + ), + this.preferenceService.get( + 'arduino.window.autoScale', + true + ), this.preferenceService.get('arduino.window.zoomLevel', 0), // this.preferenceService.get('arduino.ide.autoUpdate', true), - this.preferenceService.get('arduino.compile.verbose', true), + this.preferenceService.get( + 'arduino.compile.verbose', + true + ), this.preferenceService.get('arduino.compile.warnings', 'None'), this.preferenceService.get('arduino.upload.verbose', true), this.preferenceService.get('arduino.upload.verify', true), this.preferenceService.get('arduino.language.log', true), - this.configService.getConfiguration() + this.configService.getConfiguration(), ]); const { additionalUrls, sketchDirUri, network } = cliConfig; - const sketchbookPath = await this.fileService.fsPath(new URI(sketchDirUri)); + const sketchbookPath = await this.fileService.fsPath( + new URI(sketchDirUri) + ); return { editorFontSize, themeId, @@ -131,7 +156,7 @@ export class SettingsService { enableLsLogs, additionalUrls, sketchbookPath, - network + network, }; } @@ -140,7 +165,7 @@ export class SettingsService { return this._settings; } - async update(settings: Settings, fireDidChange: boolean = false): Promise { + async update(settings: Settings, fireDidChange = false): Promise { await this.ready.promise; for (const key of Object.keys(settings)) { this._settings[key] = settings[key]; @@ -155,17 +180,25 @@ export class SettingsService { return this.update(settings, true); } - async validate(settings: MaybePromise = this.settings()): Promise { + async validate( + settings: MaybePromise = this.settings() + ): Promise { try { const { sketchbookPath, editorFontSize, themeId } = await settings; - const sketchbookDir = await this.fileSystemExt.getUri(sketchbookPath); - if (!await this.fileService.exists(new URI(sketchbookDir))) { + const sketchbookDir = await this.fileSystemExt.getUri( + sketchbookPath + ); + if (!(await this.fileService.exists(new URI(sketchbookDir)))) { return `Invalid sketchbook location: ${sketchbookPath}`; } if (editorFontSize <= 0) { return 'Invalid editor font size. It must be a positive integer.'; } - if (!ThemeService.get().getThemes().find(({ id }) => id === themeId)) { + if ( + !ThemeService.get() + .getThemes() + .find(({ id }) => id === themeId) + ) { return 'Invalid theme.'; } return true; @@ -194,54 +227,112 @@ export class SettingsService { enableLsLogs, sketchbookPath, additionalUrls, - network + network, } = this._settings; const [config, sketchDirUri] = await Promise.all([ this.configService.getConfiguration(), - this.fileSystemExt.getUri(sketchbookPath) + this.fileSystemExt.getUri(sketchbookPath), ]); (config as any).additionalUrls = additionalUrls; (config as any).sketchDirUri = sketchDirUri; (config as any).network = network; await Promise.all([ - this.preferenceService.set('editor.fontSize', editorFontSize, PreferenceScope.User), - this.preferenceService.set('workbench.colorTheme', themeId, PreferenceScope.User), - this.preferenceService.set('editor.autoSave', autoSave, PreferenceScope.User), - this.preferenceService.set('editor.quickSuggestions', quickSuggestions, PreferenceScope.User), - this.preferenceService.set('arduino.window.autoScale', autoScaleInterface, PreferenceScope.User), - this.preferenceService.set('arduino.window.zoomLevel', interfaceScale, PreferenceScope.User), + this.preferenceService.set( + 'editor.fontSize', + editorFontSize, + PreferenceScope.User + ), + this.preferenceService.set( + 'workbench.colorTheme', + themeId, + PreferenceScope.User + ), + this.preferenceService.set( + 'editor.autoSave', + autoSave, + PreferenceScope.User + ), + this.preferenceService.set( + 'editor.quickSuggestions', + quickSuggestions, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.window.autoScale', + autoScaleInterface, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.window.zoomLevel', + interfaceScale, + PreferenceScope.User + ), // this.preferenceService.set('arduino.ide.autoUpdate', checkForUpdates, PreferenceScope.User), - this.preferenceService.set('arduino.compile.verbose', verboseOnCompile, PreferenceScope.User), - this.preferenceService.set('arduino.compile.warnings', compilerWarnings, PreferenceScope.User), - this.preferenceService.set('arduino.upload.verbose', verboseOnUpload, PreferenceScope.User), - this.preferenceService.set('arduino.upload.verify', verifyAfterUpload, PreferenceScope.User), - this.preferenceService.set('arduino.language.log', enableLsLogs, PreferenceScope.User), - this.configService.setConfiguration(config) + this.preferenceService.set( + 'arduino.compile.verbose', + verboseOnCompile, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.compile.warnings', + compilerWarnings, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.upload.verbose', + verboseOnUpload, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.upload.verify', + verifyAfterUpload, + PreferenceScope.User + ), + this.preferenceService.set( + 'arduino.language.log', + enableLsLogs, + PreferenceScope.User + ), + this.configService.setConfiguration(config), ]); this.onDidChangeEmitter.fire(this._settings); return true; } - } -export class SettingsComponent extends React.Component { - +export class SettingsComponent extends React.Component< + SettingsComponent.Props, + SettingsComponent.State +> { readonly toDispose = new DisposableCollection(); constructor(props: SettingsComponent.Props) { super(props); } - componentDidUpdate(_: SettingsComponent.Props, prevState: SettingsComponent.State): void { - if (this.state && prevState && JSON.stringify(this.state) !== JSON.stringify(prevState)) { + componentDidUpdate( + _: SettingsComponent.Props, + prevState: SettingsComponent.State + ): void { + if ( + this.state && + prevState && + JSON.stringify(this.state) !== JSON.stringify(prevState) + ) { this.props.settingsService.update(this.state, true); } } componentDidMount(): void { - this.props.settingsService.settings().then(settings => this.setState(settings)); - this.toDispose.push(this.props.settingsService.onDidChange(settings => this.setState(settings))); + this.props.settingsService + .settings() + .then((settings) => this.setState(settings)); + this.toDispose.push( + this.props.settingsService.onDidChange((settings) => + this.setState(settings) + ) + ); } componentWillUnmount(): void { @@ -252,236 +343,320 @@ export class SettingsComponent extends React.Component; } - return - - Settings - Network - - - {this.renderSettings()} - - - {this.renderNetwork()} - - ; + return ( + + + Settings + Network + + {this.renderSettings()} + {this.renderNetwork()} + + ); } protected renderSettings(): React.ReactNode { - return
- Sketchbook location: -
- - -
-
-
-
Editor font size:
-
Interface scale:
-
Theme:
-
Show verbose output during:
-
Compiler warnings:
+ return ( +
+ Sketchbook location: +
+ +
-
-
- +
+
+
Editor font size:
+
Interface scale:
+
Theme:
+
+ Show verbose output during: +
+
Compiler warnings:
-
-
+ + + + + +
+ Additional boards manager URLs: + + +
- - - - - -
- Additional boards manager URLs: - - -
-
; + ); } protected renderNetwork(): React.ReactNode { - return
-
- - -
- {this.renderProxySettings()} -
; + return ( +
+
+ + +
+ {this.renderProxySettings()} +
+ ); } protected renderProxySettings(): React.ReactNode { const disabled = this.state.network === 'none'; - return -
-
- - HTTP -