diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index a7478a89f..a493028d0 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -37,11 +37,13 @@ body: id: os attributes: label: Operating system - description: Which operating system are you using on your computer? + description: Which operating system(s) are you using on your computer? + multiple: true options: - Windows - Linux - macOS + - N/A validations: required: true - type: input diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 63dd34b78..cdc54c3b6 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -32,11 +32,13 @@ body: id: os attributes: label: Operating system - description: Which operating system are you using on your computer? + description: Which operating system(s) are you using on your computer? + multiple: true options: - Windows - Linux - macOS + - N/A validations: required: true - type: input diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24a0b30aa..592b8fc2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,6 +78,7 @@ jobs: fi fi + npx node-gyp install yarn --cwd ./electron/packager/ yarn --cwd ./electron/packager/ package diff --git a/.github/workflows/themes-weekly-pull.yml b/.github/workflows/themes-weekly-pull.yml new file mode 100644 index 000000000..8eb579674 --- /dev/null +++ b/.github/workflows/themes-weekly-pull.yml @@ -0,0 +1,49 @@ +name: themes-weekly-pull + +on: + schedule: + # run every friday at 5AM + - cron: '0 5 * * 5' + workflow_dispatch: + +env: + NODE_VERSION: 14.x + +jobs: + pull-from-jsonbin: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: yarn + + - name: Run themes:pull script + run: yarn run themes:pull + env: + JSONBIN_MASTER_KEY: ${{ secrets.JSONBIN_MASTER_KEY }} + JSONBIN_ID: ${{ secrets.JSONBIN_ID }} + + - name: Generate dark tokens + run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/dark.json core,ide-default,ide-dark,theia core,ide-default,ide-dark + + - name: Generate default tokens + run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/default.json core,ide-default,theia core,ide-default + + - name: Run themes:generate script + run: yarn run themes:generate + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + commit-message: Updated themes + title: Update themes + branch: themes/themes-update + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> diff --git a/.gitignore b/.gitignore index fdab6fb0e..4380cce54 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,9 @@ yarn*.log plugins # the config files for the CLI arduino-ide-extension/data/cli/config +# the tokens folder for the themes +scripts/themes/tokens +# environment variables +.env +# content trace files for electron +electron-app/traces diff --git a/.prettierrc b/.prettierrc index b20f01f1c..47e5c04d7 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "singleQuote": true, "tabWidth": 2, "useTabs": false, - "printWidth": 80 + "printWidth": 80, + "endOfLine": "auto" } diff --git a/.vscode/launch.json b/.vscode/launch.json index d6ed25954..802b130d6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,44 @@ { "version": "0.2.0", "configurations": [ + { + "type": "node", + "request": "launch", + "name": "App (Electron) [Dev]", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", + "windows": { + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd", + }, + "cwd": "${workspaceFolder}/electron-app", + "args": [ + ".", + "--log-level=debug", + "--hostname=localhost", + "--no-cluster", + "--app-project-path=${workspaceRoot}/electron-app", + "--remote-debugging-port=9222", + "--no-app-auto-install", + "--plugins=local-dir:../plugins", + "--hosted-plugin-inspect=9339", + "--nosplash", + "--content-trace", + "--open-devtools" + ], + "env": { + "NODE_ENV": "development" + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/electron-app/src-gen/backend/*.js", + "${workspaceRoot}/electron-app/src-gen/frontend/*.js", + "${workspaceRoot}/electron-app/lib/**/*.js", + "${workspaceRoot}/arduino-ide-extension/lib/**/*.js", + "${workspaceRoot}/node_modules/@theia/**/*.js" + ], + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, { "type": "node", "request": "launch", @@ -10,7 +48,6 @@ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd", }, "cwd": "${workspaceFolder}/electron-app", - "protocol": "inspector", "args": [ ".", "--log-level=debug", @@ -78,7 +115,6 @@ { "type": "node", "request": "launch", - "protocol": "inspector", "name": "Run Test [current]", "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", "args": [ diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 426665701..143c7d6c5 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -1,6 +1,6 @@ { "name": "arduino-ide-extension", - "version": "2.0.0-rc6", + "version": "2.0.0-rc7", "description": "An extension for Theia building the Arduino IDE", "license": "AGPL-3.0-or-later", "scripts": { @@ -21,31 +21,30 @@ "test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\"" }, "dependencies": { - "@grpc/grpc-js": "^1.3.7", - "@theia/application-package": "1.22.1", - "@theia/core": "1.22.1", - "@theia/editor": "1.22.1", - "@theia/editor-preview": "1.22.1", - "@theia/electron": "1.22.1", - "@theia/filesystem": "1.22.1", - "@theia/git": "1.22.1", - "@theia/keymaps": "1.22.1", - "@theia/markers": "1.22.1", - "@theia/monaco": "1.22.1", - "@theia/navigator": "1.22.1", - "@theia/outline-view": "1.22.1", - "@theia/output": "1.22.1", - "@theia/preferences": "1.22.1", - "@theia/search-in-workspace": "1.22.1", - "@theia/terminal": "1.22.1", - "@theia/workspace": "1.22.1", + "@grpc/grpc-js": "^1.6.7", + "@theia/application-package": "1.25.0", + "@theia/core": "1.25.0", + "@theia/editor": "1.25.0", + "@theia/editor-preview": "1.25.0", + "@theia/electron": "1.25.0", + "@theia/filesystem": "1.25.0", + "@theia/keymaps": "1.25.0", + "@theia/markers": "1.25.0", + "@theia/monaco": "1.25.0", + "@theia/navigator": "1.25.0", + "@theia/outline-view": "1.25.0", + "@theia/output": "1.25.0", + "@theia/preferences": "1.25.0", + "@theia/search-in-workspace": "1.25.0", + "@theia/terminal": "1.25.0", + "@theia/workspace": "1.25.0", "@tippyjs/react": "^4.2.5", "@types/atob": "^2.1.2", "@types/auth0-js": "^9.14.0", "@types/btoa": "^1.2.3", "@types/dateformat": "^3.0.1", "@types/deepmerge": "^2.2.0", - "@types/glob": "^5.0.35", + "@types/glob": "^7.2.0", "@types/google-protobuf": "^3.7.2", "@types/js-yaml": "^3.12.2", "@types/keytar": "^4.4.0", @@ -58,19 +57,17 @@ "@types/temp": "^0.8.34", "@types/which": "^1.3.1", "ajv": "^6.5.3", - "arduino-serial-plotter-webapp": "0.0.17", + "arduino-serial-plotter-webapp": "0.1.0", "async-mutex": "^0.3.0", "atob": "^2.1.2", "auth0-js": "^9.14.0", "btoa": "^1.2.1", - "css-element-queries": "^1.2.0", "dateformat": "^3.0.3", "deepmerge": "2.0.1", "electron-updater": "^4.6.5", - "fuzzy": "^0.1.3", + "fast-safe-stringify": "^2.1.1", "glob": "^7.1.6", - "google-protobuf": "^3.11.4", - "grpc": "^1.24.11", + "google-protobuf": "^3.20.1", "hash.js": "^1.1.7", "is-valid-path": "^0.1.1", "js-yaml": "^3.13.1", @@ -91,6 +88,7 @@ "semver": "^7.3.2", "string-natural-compare": "^2.0.3", "temp": "^0.9.1", + "temp-dir": "^2.0.0", "tree-kill": "^1.2.1", "upath": "^1.1.2", "url": "^0.11.0", @@ -157,13 +155,13 @@ ], "arduino": { "cli": { - "version": "0.21.0" + "version": "0.23.0" }, "fwuploader": { - "version": "2.0.0" + "version": "2.2.0" }, "clangd": { - "version": "13.0.0" + "version": "14.0.0" }, "languageServer": { "version": "0.6.0" diff --git a/arduino-ide-extension/scripts/download-examples.js b/arduino-ide-extension/scripts/download-examples.js index a393c5552..c7bcf9865 100644 --- a/arduino-ide-extension/scripts/download-examples.js +++ b/arduino-ide-extension/scripts/download-examples.js @@ -4,30 +4,93 @@ const version = '1.9.1'; (async () => { + const os = require('os'); + const { promises: fs } = require('fs'); + const path = require('path'); + const shell = require('shelljs'); + const { v4 } = require('uuid'); - const os = require('os'); - const path = require('path'); - const shell = require('shelljs'); - const { v4 } = require('uuid'); + const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`); + if (shell.mkdir('-p', repository).code !== 0) { + shell.exit(1); + } - const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`); - if (shell.mkdir('-p', repository).code !== 0) { - shell.exit(1); - process.exit(1); - } - - if (shell.exec(`git clone https://github.com/arduino/arduino-examples.git ${repository}`).code !== 0) { - shell.exit(1); - process.exit(1); - } + if ( + shell.exec( + `git clone https://github.com/arduino/arduino-examples.git ${repository}` + ).code !== 0 + ) { + shell.exit(1); + } - if (shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`).code !== 0) { - shell.exit(1); - process.exit(1); - } + if ( + shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`) + .code !== 0 + ) { + shell.exit(1); + } - const destination = path.join(__dirname, '..', 'Examples'); - shell.mkdir('-p', destination); - shell.cp('-fR', path.join(repository, 'examples', '*'), destination); + const destination = path.join(__dirname, '..', 'Examples'); + shell.mkdir('-p', destination); + shell.cp('-fR', path.join(repository, 'examples', '*'), destination); + const isSketch = async (pathLike) => { + try { + const names = await fs.readdir(pathLike); + const dirName = path.basename(pathLike); + return names.indexOf(`${dirName}.ino`) !== -1; + } catch (e) { + if (e.code === 'ENOTDIR') { + return false; + } + throw e; + } + }; + const examples = []; + const categories = await fs.readdir(destination); + const visit = async (pathLike, container) => { + const stat = await fs.lstat(pathLike); + if (stat.isDirectory()) { + if (await isSketch(pathLike)) { + container.sketches.push({ + name: path.basename(pathLike), + relativePath: path.relative(destination, pathLike), + }); + } else { + const names = await fs.readdir(pathLike); + for (const name of names) { + const childPath = path.join(pathLike, name); + if (await isSketch(childPath)) { + container.sketches.push({ + name, + relativePath: path.relative(destination, childPath), + }); + } else { + const child = { + label: name, + children: [], + sketches: [], + }; + container.children.push(child); + await visit(childPath, child); + } + } + } + } + }; + for (const category of categories) { + const example = { + label: category, + children: [], + sketches: [], + }; + await visit(path.join(destination, category), example); + examples.push(example); + } + await fs.writeFile( + path.join(destination, 'examples.json'), + JSON.stringify(examples, null, 2), + { encoding: 'utf8' } + ); + shell.echo(`Generated output to ${path.join(destination, 'examples.json')}`); })(); diff --git a/arduino-ide-extension/scripts/download-ls.js b/arduino-ide-extension/scripts/download-ls.js index f00a8e625..81b932950 100755 --- a/arduino-ide-extension/scripts/download-ls.js +++ b/arduino-ide-extension/scripts/download-ls.js @@ -66,21 +66,24 @@ build, `arduino-language-server${platform === 'win32' ? '.exe' : ''}` ); - let clangdExecutablePath, lsSuffix, clangdSuffix; + let clangdExecutablePath, clangFormatExecutablePath, lsSuffix, clangdSuffix; switch (platformArch) { case 'darwin-x64': clangdExecutablePath = path.join(build, 'clangd'); + clangFormatExecutablePath = path.join(build, 'clang-format'); lsSuffix = 'macOS_64bit.tar.gz'; clangdSuffix = 'macOS_64bit'; break; case 'linux-x64': clangdExecutablePath = path.join(build, 'clangd'); + clangFormatExecutablePath = path.join(build, 'clang-format'); lsSuffix = 'Linux_64bit.tar.gz'; clangdSuffix = 'Linux_64bit'; break; case 'win32-x64': clangdExecutablePath = path.join(build, 'clangd.exe'); + clangFormatExecutablePath = path.join(build, 'clang-format.exe'); lsSuffix = 'Windows_64bit.zip'; clangdSuffix = 'Windows_64bit'; break; @@ -103,4 +106,15 @@ downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, { strip: 1, }); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder. + + const clangdFormatUrl = `https://downloads.arduino.cc/tools/clang-format_${clangdVersion}_${clangdSuffix}.tar.bz2`; + downloader.downloadUnzipAll( + clangdFormatUrl, + build, + clangFormatExecutablePath, + force, + { + strip: 1, + } + ); })(); diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 18f4df1a1..eeb25f737 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -1,5 +1,9 @@ -import { inject, injectable, postConstruct } from 'inversify'; -import * as React from 'react'; +import { + inject, + injectable, + postConstruct, +} from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { BoardsService, @@ -7,6 +11,7 @@ import { ExecutableService, Sketch, LibraryService, + ArduinoDaemon, } from '../common/protocol'; import { Mutex } from 'async-mutex'; import { @@ -17,9 +22,11 @@ import { DisposableCollection, } from '@theia/core'; import { + Dialog, FrontendApplication, FrontendApplicationContribution, LocalStorageService, + OnWillStopAction, SaveableWidget, StatusBar, StatusBarAlignment, @@ -44,19 +51,12 @@ import { EditorManager, EditorOpenerOptions, } from '@theia/editor/lib/browser'; -import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; -import { FileNavigatorCommands, FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution'; -import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution'; -import { OutputContribution } from '@theia/output/lib/browser/output-contribution'; -import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution'; -import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution'; +import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; -import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileChangeType } from '@theia/filesystem/lib/browser'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; -import { ConfigService } from '../common/protocol/config-service'; import { ArduinoCommands } from './arduino-commands'; import { BoardsConfig } from './boards/boards-config'; import { BoardsConfigDialog } from './boards/boards-config-dialog'; @@ -67,12 +67,15 @@ import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; import { ArduinoPreferences } from './arduino-preferences'; -import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl'; +import { + CurrentSketch, + SketchesServiceClientImpl, +} from '../common/protocol/sketches-service-client-impl'; import { SaveAsSketch } from './contributions/save-as-sketch'; -import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution'; import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { IDEUpdater } from '../common/protocol/ide-updater'; import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; +import { HostedPluginEvents } from './hosted-plugin-events'; const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; export const SKIP_IDE_VERSION = 'skipIDEVersion'; @@ -84,92 +87,73 @@ export class ArduinoFrontendContribution TabBarToolbarContribution, CommandContribution, MenuContribution, - ColorContribution { + ColorContribution +{ @inject(ILogger) - protected logger: ILogger; + private readonly logger: ILogger; @inject(MessageService) - protected readonly messageService: MessageService; + private readonly messageService: MessageService; @inject(BoardsService) - protected readonly boardsService: BoardsService; + private readonly boardsService: BoardsService; @inject(LibraryService) - protected readonly libraryService: LibraryService; + private readonly libraryService: LibraryService; @inject(BoardsServiceProvider) - protected readonly boardsServiceClientImpl: BoardsServiceProvider; + private readonly boardsServiceClientImpl: BoardsServiceProvider; @inject(EditorManager) - protected readonly editorManager: EditorManager; + private readonly editorManager: EditorManager; @inject(FileService) - protected readonly fileService: FileService; + private readonly fileService: FileService; @inject(SketchesService) - protected readonly sketchService: SketchesService; + private readonly sketchService: SketchesService; @inject(BoardsConfigDialog) - protected readonly boardsConfigDialog: BoardsConfigDialog; + private readonly boardsConfigDialog: BoardsConfigDialog; @inject(CommandRegistry) - protected readonly commandRegistry: CommandRegistry; + private readonly commandRegistry: CommandRegistry; @inject(StatusBar) - protected readonly statusBar: StatusBar; - - @inject(FileNavigatorContribution) - protected readonly fileNavigatorContributions: FileNavigatorContribution; - - @inject(OutputContribution) - protected readonly outputContribution: OutputContribution; - - @inject(OutlineViewContribution) - protected readonly outlineContribution: OutlineViewContribution; - - @inject(ProblemContribution) - protected readonly problemContribution: ProblemContribution; - - @inject(ScmContribution) - protected readonly scmContribution: ScmContribution; - - @inject(SearchInWorkspaceFrontendContribution) - protected readonly siwContribution: SearchInWorkspaceFrontendContribution; - - @inject(SketchbookWidgetContribution) - protected readonly sketchbookWidgetContribution: SketchbookWidgetContribution; + private readonly statusBar: StatusBar; @inject(EditorMode) - protected readonly editorMode: EditorMode; - - @inject(ConfigService) - protected readonly configService: ConfigService; + private readonly editorMode: EditorMode; - @inject(HostedPluginSupport) - protected hostedPluginSupport: HostedPluginSupport; + @inject(HostedPluginEvents) + private readonly hostedPluginEvents: HostedPluginEvents; @inject(ExecutableService) - protected executableService: ExecutableService; + private readonly executableService: ExecutableService; @inject(ArduinoPreferences) - protected readonly arduinoPreferences: ArduinoPreferences; + private readonly arduinoPreferences: ArduinoPreferences; @inject(SketchesServiceClientImpl) - protected readonly sketchServiceClient: SketchesServiceClientImpl; + private readonly sketchServiceClient: SketchesServiceClientImpl; - protected readonly appStateService: FrontendApplicationStateService; + @inject(FrontendApplicationStateService) + private readonly appStateService: FrontendApplicationStateService; @inject(LocalStorageService) - protected readonly localStorageService: LocalStorageService; + private readonly localStorageService: LocalStorageService; @inject(FileSystemFrontendContribution) - protected readonly fileSystemFrontendContribution: FileSystemFrontendContribution; + private readonly fileSystemFrontendContribution: FileSystemFrontendContribution; @inject(IDEUpdater) - protected readonly updater: IDEUpdater; + private readonly updater: IDEUpdater; @inject(IDEUpdaterDialog) - protected readonly updaterDialog: IDEUpdaterDialog; + private readonly updaterDialog: IDEUpdaterDialog; + + @inject(ArduinoDaemon) + private readonly daemon: ArduinoDaemon; protected invalidConfigPopup: | Promise @@ -240,7 +224,10 @@ export class ArduinoFrontendContribution updateStatusBar(this.boardsServiceClientImpl.boardsConfig); this.appStateService.reachedState('ready').then(async () => { const sketch = await this.sketchServiceClient.currentSketch(); - if (sketch && !(await this.sketchService.isTemp(sketch))) { + if ( + CurrentSketch.isValid(sketch) && + !(await this.sketchService.isTemp(sketch)) + ) { this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); this.toDisposeOnStop.push( this.fileService.onDidFilesChange(async (event) => { @@ -266,21 +253,6 @@ export class ArduinoFrontendContribution } async onStart(app: FrontendApplication): Promise { - // Initialize all `pro-mode` widgets. This is a NOOP if in normal mode. - for (const viewContribution of [ - this.fileNavigatorContributions, - this.outputContribution, - this.outlineContribution, - this.problemContribution, - this.scmContribution, - this.siwContribution, - this.sketchbookWidgetContribution, - ] as Array) { - if (viewContribution.initializeLayout) { - viewContribution.initializeLayout(app); - } - } - this.updater .init( this.arduinoPreferences.get('arduino.ide.updateChannel'), @@ -314,6 +286,12 @@ export class ArduinoFrontendContribution } }; this.boardsServiceClientImpl.onBoardsConfigChanged(start); + this.hostedPluginEvents.onPluginsDidStart(() => + start(this.boardsServiceClientImpl.boardsConfig) + ); + this.hostedPluginEvents.onPluginsWillUnload( + () => (this.languageServerFqbn = undefined) + ); this.arduinoPreferences.onPreferenceChanged((event) => { if (event.newValue !== event.oldValue) { switch (event.preferenceName) { @@ -344,16 +322,18 @@ export class ArduinoFrontendContribution app.shell.leftPanelHandler.removeBottomMenu('settings-menu'); - this.fileSystemFrontendContribution.onDidChangeEditorFile(e => { - if (e.type === FileChangeType.DELETED) { - const editorWidget = e.editor; - if (SaveableWidget.is(editorWidget)) { - editorWidget.closeWithoutSaving(); - } else { - editorWidget.close(); + this.fileSystemFrontendContribution.onDidChangeEditorFile( + ({ type, editor }) => { + if (type === FileChangeType.DELETED) { + const editorWidget = editor; + if (SaveableWidget.is(editorWidget)) { + editorWidget.closeWithoutSaving(); + } else { + editorWidget.close(); + } } } - }); + ); } onStop(): void { @@ -366,9 +346,13 @@ export class ArduinoFrontendContribution fqbn: string, name: string | undefined ): Promise { + const port = await this.daemon.tryGetPort(); + if (!port) { + return; + } const release = await this.languageServerStartMutex.acquire(); try { - await this.hostedPluginSupport.didStart; + await this.hostedPluginEvents.didStart; const details = await this.boardsService.getBoardDetails({ fqbn }); if (!details) { // Core is not installed for the selected board. @@ -403,7 +387,7 @@ export class ArduinoFrontendContribution let currentSketchPath: string | undefined = undefined; if (log) { const currentSketch = await this.sketchServiceClient.currentSketch(); - if (currentSketch) { + if (CurrentSketch.isValid(currentSketch)) { currentSketchPath = await this.fileService.fsPath( new URI(currentSketch.uri) ); @@ -415,8 +399,6 @@ export class ArduinoFrontendContribution this.fileService.fsPath(new URI(lsUri)), ]); - const config = await this.configService.getConfiguration(); - this.languageServerFqbn = await Promise.race([ new Promise((_, reject) => setTimeout( @@ -428,7 +410,7 @@ export class ArduinoFrontendContribution 'arduino.languageserver.start', { lsPath, - cliDaemonAddr: `localhost:${config.daemon.port}`, // TODO: verify if this port is coming from the BE + cliDaemonAddr: `localhost:${port}`, clangdPath, log: currentSketchPath ? currentSketchPath : log, cliDaemonInstance: '1', @@ -494,13 +476,13 @@ export class ArduinoFrontendContribution EditorCommands.SPLIT_EDITOR_UP, EditorCommands.SPLIT_EDITOR_VERTICAL, EditorCommands.SPLIT_EDITOR_HORIZONTAL, - FileNavigatorCommands.REVEAL_IN_NAVIGATOR + FileNavigatorCommands.REVEAL_IN_NAVIGATOR, ]) { registry.unregisterCommand(command); } } - registerMenus(registry: MenuModelRegistry) { + registerMenus(registry: MenuModelRegistry): void { const menuId = (menuPath: string[]): string => { const index = menuPath.length - 1; const menuId = menuPath[index]; @@ -569,12 +551,19 @@ export class ArduinoFrontendContribution uri: string, forceOpen = false, options?: EditorOpenerOptions | undefined - ): Promise { + ): Promise { const widget = this.editorManager.all.find( (widget) => widget.editor.uri.toString() === uri ); if (!widget || forceOpen) { - return this.editorManager.open(new URI(uri), options); + return this.editorManager.open( + new URI(uri), + options ?? { + mode: 'reveal', + preview: false, + counter: 0, + } + ); } } @@ -658,4 +647,57 @@ export class ArduinoFrontendContribution } ); } + + onWillStop(): OnWillStopAction { + return { + reason: 'temp-sketch', + action: () => { + return this.showTempSketchDialog(); + }, + }; + } + + private async showTempSketchDialog(): Promise { + const sketch = await this.sketchServiceClient.currentSketch(); + if (!CurrentSketch.isValid(sketch)) { + return true; + } + const isTemp = await this.sketchService.isTemp(sketch); + if (!isTemp) { + return true; + } + const messageBoxResult = await remote.dialog.showMessageBox( + remote.getCurrentWindow(), + { + message: nls.localize( + 'arduino/sketch/saveTempSketch', + 'Save your sketch to open it again later.' + ), + title: nls.localize( + 'theia/core/quitTitle', + 'Are you sure you want to quit?' + ), + type: 'question', + buttons: [ + Dialog.CANCEL, + nls.localizeByDefault('Save As...'), + nls.localizeByDefault("Don't Save"), + ], + } + ); + const result = messageBoxResult.response; + if (result === 2) { + return true; + } else if (result === 1) { + return !!(await this.commandRegistry.executeCommand( + SaveAsSketch.Commands.SAVE_AS_SKETCH.id, + { + execOnlyIfTemp: false, + openAfterMove: false, + wipeOriginal: true, + } + )); + } + return false; + } } 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 5ceae9179..9ef3c2423 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -1,5 +1,5 @@ import '../../src/browser/style/index.css'; -import { ContainerModule } from 'inversify'; +import { ContainerModule } from '@theia/core/shared/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'; @@ -50,7 +50,6 @@ import { ApplicationShell as TheiaApplicationShell, ShellLayoutRestorer as TheiaShellLayoutRestorer, CommonFrontendContribution as TheiaCommonFrontendContribution, - KeybindingRegistry as TheiaKeybindingRegistry, TabBarRendererFactory, ContextMenuRenderer, createTreeContainer, @@ -69,20 +68,12 @@ import { ScmContribution } from './theia/scm/scm-contribution'; import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution'; import { SearchInWorkspaceFrontendContribution } from './theia/search-in-workspace/search-in-workspace-frontend-contribution'; import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution'; -import { SerialServiceClientImpl } from './serial/serial-service-client-impl'; -import { - SerialServicePath, - SerialService, - SerialServiceClient, -} from '../common/protocol/serial-service'; import { ConfigService, ConfigServicePath, } from '../common/protocol/config-service'; import { MonitorWidget } from './serial/monitor/monitor-widget'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; -import { SerialConnectionManager } from './serial/serial-connection-manager'; -import { SerialModel } from './serial/serial-model'; import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator'; import { TabBarDecoratorService } from './theia/core/tab-bar-decorator'; import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser'; @@ -138,7 +129,6 @@ import { PreferencesContribution } from './theia/preferences/preferences-contrib import { QuitApp } from './contributions/quit-app'; import { SketchControl } from './contributions/sketch-control'; import { Settings } from './contributions/settings'; -import { KeybindingRegistry } from './theia/core/keybindings'; import { WorkspaceCommandContribution } from './theia/workspace/workspace-commands'; import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler'; import { WorkspaceDeleteHandler } from './theia/workspace/workspace-delete-handler'; @@ -160,7 +150,14 @@ import { OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl, OutputChannelRegistryMainImpl, } from './theia/plugin-ext/output-channel-registry-main'; -import { ExecutableService, ExecutableServicePath } from '../common/protocol'; +import { + ExecutableService, + ExecutableServicePath, + MonitorManagerProxy, + MonitorManagerProxyClient, + MonitorManagerProxyFactory, + MonitorManagerProxyPath, +} 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'; @@ -275,20 +272,41 @@ import { IDEUpdaterDialogWidget, } from './dialogs/ide-updater/ide-updater-dialog'; import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider'; - -const ElementQueries = require('css-element-queries/src/ElementQueries'); +import { MonitorModel } from './monitor-model'; +import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl'; +import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager'; +import { EditorManager } from './theia/editor/editor-manager'; +import { HostedPluginEvents } from './hosted-plugin-events'; +import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin'; +import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin'; +import { Formatter, FormatterPath } from '../common/protocol/formatter'; +import { Format } from './contributions/format'; +import { MonacoFormattingConflictsContribution } from './theia/monaco/monaco-formatting-conflicts'; +import { MonacoFormattingConflictsContribution as TheiaMonacoFormattingConflictsContribution } from '@theia/monaco/lib/browser/monaco-formatting-conflicts'; +import { DefaultJsonSchemaContribution } from './theia/core/json-schema-store'; +import { DefaultJsonSchemaContribution as TheiaDefaultJsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; +import { EditorNavigationContribution } from './theia/editor/editor-navigation-contribution'; +import { EditorNavigationContribution as TheiaEditorNavigationContribution } from '@theia/editor/lib/browser/editor-navigation-contribution'; +import { PreferenceTreeGenerator } from './theia/preferences/preference-tree-generator'; +import { PreferenceTreeGenerator as TheiaPreferenceTreeGenerator } from '@theia/preferences/lib/browser/util/preference-tree-generator'; +import { AboutDialog } from './theia/core/about-dialog'; +import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog'; MonacoThemingService.register({ id: 'arduino-theme', label: 'Light (Arduino)', uiTheme: 'vs', - json: require('../../src/browser/data/arduino.color-theme.json'), + json: require('../../src/browser/data/default.color-theme.json'), }); -export default new ContainerModule((bind, unbind, isBound, rebind) => { - ElementQueries.listen(); - ElementQueries.init(); +MonacoThemingService.register({ + id: 'arduino-theme-dark', + label: 'Dark (Arduino)', + uiTheme: 'vs-dark', + json: require('../../src/browser/data/dark.color-theme.json'), +}); +export default new ContainerModule((bind, unbind, isBound, rebind) => { // Commands and toolbar items bind(ArduinoFrontendContribution).toSelf().inSingletonScope(); bind(CommandContribution).toService(ArduinoFrontendContribution); @@ -407,29 +425,44 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); // Serial monitor - bind(SerialModel).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(SerialModel); bind(MonitorWidget).toSelf(); + bind(FrontendApplicationContribution).toService(MonitorModel); + bind(MonitorModel).toSelf().inSingletonScope(); bindViewContribution(bind, MonitorViewContribution); bind(TabBarToolbarContribution).toService(MonitorViewContribution); bind(WidgetFactory).toDynamicValue((context) => ({ id: MonitorWidget.ID, - createWidget: () => context.container.get(MonitorWidget), - })); - // Frontend binding for the serial service - bind(SerialService) - .toDynamicValue((context) => { - const connection = context.container.get(WebSocketConnectionProvider); - const client = context.container.get( - SerialServiceClient + createWidget: () => { + return new MonitorWidget( + context.container.get(MonitorModel), + context.container.get( + MonitorManagerProxyClient + ), + context.container.get(BoardsServiceProvider) ); - return connection.createProxy(SerialServicePath, client); - }) + }, + })); + + bind(MonitorManagerProxyFactory).toFactory( + (context) => () => + context.container.get(MonitorManagerProxy) + ); + + bind(MonitorManagerProxy) + .toDynamicValue((context) => + WebSocketConnectionProvider.createProxy( + context.container, + MonitorManagerProxyPath, + context.container.get(MonitorManagerProxyClient) + ) + ) .inSingletonScope(); - bind(SerialConnectionManager).toSelf().inSingletonScope(); - // Serial service client to receive and delegate notifications from the backend. - bind(SerialServiceClient).to(SerialServiceClientImpl).inSingletonScope(); + // Monitor manager proxy client to receive and delegate pluggable monitors + // notifications from the backend + bind(MonitorManagerProxyClient) + .to(MonitorManagerProxyClientImpl) + .inSingletonScope(); bind(WorkspaceService).toSelf().inSingletonScope(); rebind(TheiaWorkspaceService).toService(WorkspaceService); @@ -477,7 +510,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { rebind(TheiaPreferencesContribution) .to(PreferencesContribution) .inSingletonScope(); - rebind(TheiaKeybindingRegistry).to(KeybindingRegistry).inSingletonScope(); rebind(TheiaWorkspaceCommandContribution) .to(WorkspaceCommandContribution) .inSingletonScope(); @@ -486,11 +518,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope(); rebind(TabBarToolbarFactory).toFactory( - ({ container: parentContainer }) => () => { - const container = parentContainer.createChild(); - container.bind(TabBarToolbar).toSelf().inSingletonScope(); - return container.get(TabBarToolbar); - } + ({ container: parentContainer }) => + () => { + const container = parentContainer.createChild(); + container.bind(TabBarToolbar).toSelf().inSingletonScope(); + return container.get(TabBarToolbar); + } ); bind(OutputWidget).toSelf().inSingletonScope(); rebind(TheiaOutputWidget).toService(OutputWidget); @@ -508,6 +541,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(SearchInWorkspaceWidget).toSelf(); rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget); + // Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times. + bind(EditorManager).toSelf().inSingletonScope(); + rebind(TheiaEditorManager).to(EditorManager); + // replace search icon rebind(TheiaSearchInWorkspaceFactory) .to(SearchInWorkspaceFactory) @@ -559,6 +596,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { ) .inSingletonScope(); + bind(Formatter) + .toDynamicValue(({ container }) => + WebSocketConnectionProvider.createProxy(container, FormatterPath) + ) + .inSingletonScope(); + bind(ArduinoFirmwareUploader) .toDynamicValue((context) => WebSocketConnectionProvider.createProxy( @@ -626,6 +669,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, ArchiveSketch); Contribution.configure(bind, AddZipLibrary); Contribution.configure(bind, PlotterFrontendContribution); + Contribution.configure(bind, Format); + + // Disabled the quick-pick customization from Theia when multiple formatters are available. + // Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors. + bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope(); + rebind(TheiaMonacoFormattingConflictsContribution).toService( + MonacoFormattingConflictsContribution + ); bind(ResponseServiceImpl) .toSelf() @@ -655,15 +706,13 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // Enable the dirty indicator on uncloseable widgets. rebind(TabBarRendererFactory).toFactory((context) => () => { - const contextMenuRenderer = context.container.get( - ContextMenuRenderer - ); + const contextMenuRenderer = + context.container.get(ContextMenuRenderer); const decoratorService = context.container.get( TabBarDecoratorService ); - const iconThemeService = context.container.get( - IconThemeService - ); + const iconThemeService = + context.container.get(IconThemeService); return new TabBarRenderer( contextMenuRenderer, decoratorService, @@ -673,6 +722,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // Workaround for https://github.com/eclipse-theia/theia/issues/8722 // Do not trigger a save on IDE startup if `"editor.autoSave": "on"` was set as a preference. + // Note: `"editor.autoSave" was renamed to `"files.autoSave" and `"on"` was replaced with three + // different cases, but we treat `!== 'off'` as auto save enabled. (https://github.com/eclipse-theia/theia/issues/10812) bind(EditorCommandContribution).toSelf().inSingletonScope(); rebind(TheiaEditorCommandContribution).toService(EditorCommandContribution); @@ -680,6 +731,26 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NavigatorTabBarDecorator).toSelf().inSingletonScope(); rebind(TheiaNavigatorTabBarDecorator).toService(NavigatorTabBarDecorator); + // Do not fetch the `catalog.json` from Azure on FE load. + bind(DefaultJsonSchemaContribution).toSelf().inSingletonScope(); + rebind(TheiaDefaultJsonSchemaContribution).toService( + DefaultJsonSchemaContribution + ); + + // Do not block the app startup when initializing the editor navigation history. + bind(EditorNavigationContribution).toSelf().inSingletonScope(); + rebind(TheiaEditorNavigationContribution).toService( + EditorNavigationContribution + ); + + // IDE2 does not use the Theia preferences widget, no need to create and sync the underlying tree model. + bind(PreferenceTreeGenerator).toSelf().inSingletonScope(); + rebind(TheiaPreferenceTreeGenerator).toService(PreferenceTreeGenerator); + + // IDE2 has a custom about dialog, so there is no need to load the Theia extensions on FE load + bind(AboutDialog).toSelf().inSingletonScope(); + rebind(TheiaAboutDialog).toService(AboutDialog); + // To avoid running `Save All` when there are no dirty editors before starting the debug session. bind(DebugSessionManager).toSelf().inSingletonScope(); rebind(TheiaDebugSessionManager).toService(DebugSessionManager); @@ -799,4 +870,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { ); }) .inSingletonScope(); + + bind(HostedPluginSupport).toSelf().inSingletonScope(); + rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport); + bind(HostedPluginEvents).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(HostedPluginEvents); }); diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index ba6bc83c4..0aec020d5 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -1,4 +1,4 @@ -import { interfaces } from 'inversify'; +import { interfaces } from '@theia/core/shared/inversify'; import { createPreferenceProxy, PreferenceProxy, @@ -83,7 +83,7 @@ export const ArduinoConfigSchema: PreferenceSchema = { default: 'https://downloads.arduino.cc/arduino-ide', description: nls.localize( 'arduino/preferences/ide.updateBaseUrl', - `The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'` + "The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'" ), }, 'arduino.board.certificates': { diff --git a/arduino-ide-extension/src/browser/auth/authentication-client-service.ts b/arduino-ide-extension/src/browser/auth/authentication-client-service.ts index 2b0fb9cdf..4411a8eb5 100644 --- a/arduino-ide-extension/src/browser/auth/authentication-client-service.ts +++ b/arduino-ide-extension/src/browser/auth/authentication-client-service.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; @@ -43,13 +43,15 @@ export class AuthenticationClientService readonly onSessionDidChange = this.onSessionDidChangeEmitter.event; - onStart(): void { + async onStart(): Promise { this.toDispose.push(this.onSessionDidChangeEmitter); this.service.setClient(this); this.service .session() .then((session) => this.notifySessionDidChange(session)); - this.setOptions(); + + this.setOptions().then(() => this.service.initAuthSession()); + this.arduinoPreferences.onPreferenceChanged((event) => { if (event.preferenceName.startsWith('arduino.auth.')) { this.setOptions(); @@ -57,8 +59,8 @@ export class AuthenticationClientService }); } - setOptions(): void { - this.service.setOptions({ + setOptions(): Promise { + return this.service.setOptions({ redirectUri: `http://localhost:${serverPort}/callback`, responseType: 'code', clientID: this.arduinoPreferences['arduino.auth.clientID'], 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 97fefb84d..6557ba84c 100644 --- a/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts +++ b/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts @@ -1,4 +1,4 @@ -import { injectable, inject } from 'inversify'; +import { injectable, inject } from '@theia/core/shared/inversify'; import { MessageService } from '@theia/core/lib/common/message-service'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { 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 a4e962546..7ad65697a 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 @@ -1,5 +1,5 @@ -import * as React from 'react'; -import { injectable, inject } from 'inversify'; +import * as React from '@theia/core/shared/react'; +import { injectable, inject } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { ReactWidget, Message } from '@theia/core/lib/browser'; import { BoardsService } from '../../common/protocol/boards-service'; @@ -55,12 +55,13 @@ export class BoardsConfigDialogWidget extends ReactWidget { onConfigChange={this.fireConfigChanged} onFocusNodeSet={this.setFocusNode} onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event} + onAppStateDidChange={this.notificationCenter.onAppStateDidChange} /> ); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); if (this.focusNode instanceof HTMLInputElement) { this.focusNode.select(); 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 545e8067a..d5db717c8 100644 --- a/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts +++ b/arduino-ide-extension/src/browser/boards/boards-config-dialog.ts @@ -1,5 +1,5 @@ -import { injectable, inject, postConstruct } from 'inversify'; -import { Message } from '@phosphor/messaging'; +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { Message } from '@theia/core/shared/@phosphor/messaging'; import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser'; import { AbstractDialog } from '../theia/dialogs/dialogs'; import { BoardsConfig } from './boards-config'; @@ -26,7 +26,7 @@ export class BoardsConfigDialog extends AbstractDialog { constructor( @inject(BoardsConfigDialogProps) - protected readonly props: BoardsConfigDialogProps + protected override readonly props: BoardsConfigDialogProps ) { super(props); @@ -52,7 +52,7 @@ export class BoardsConfigDialog extends AbstractDialog { /** * Pass in an empty string if you want to reset the search term. Using `undefined` has no effect. */ - async open( + override async open( query: string | undefined = undefined ): Promise { if (typeof query === 'string') { @@ -95,7 +95,7 @@ export class BoardsConfigDialog extends AbstractDialog { return head; } - protected onAfterAttach(msg: Message): void { + protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } @@ -110,23 +110,23 @@ export class BoardsConfigDialog extends AbstractDialog { this.update(); } - protected onUpdateRequest(msg: Message) { + protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); this.widget.activate(); } - protected handleEnter(event: KeyboardEvent): boolean | void { + protected override handleEnter(event: KeyboardEvent): boolean | void { if (event.target instanceof HTMLTextAreaElement) { return false; } } - protected isValid(value: BoardsConfig.Config): DialogError { + protected override isValid(value: BoardsConfig.Config): DialogError { if (!value.selectedBoard) { if (value.selectedPort) { return nls.localize( diff --git a/arduino-ide-extension/src/browser/boards/boards-config.tsx b/arduino-ide-extension/src/browser/boards/boards-config.tsx index 392710c6d..1a80ced5d 100644 --- a/arduino-ide-extension/src/browser/boards/boards-config.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-config.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; import { Event } from '@theia/core/lib/common/event'; import { notEmpty } from '@theia/core/lib/common/objects'; import { MaybePromise } from '@theia/core/lib/common/types'; @@ -16,6 +16,7 @@ import { } from './boards-service-provider'; import { naturalCompare } from '../../common/utils'; import { nls } from '@theia/core/lib/common'; +import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state'; export namespace BoardsConfig { export interface Config { @@ -29,6 +30,7 @@ export namespace BoardsConfig { readonly onConfigChange: (config: Config) => void; readonly onFocusNodeSet: (element: HTMLElement | undefined) => void; readonly onFilteredTextDidChangeEvent: Event; + readonly onAppStateDidChange: Event; } export interface State extends Config { @@ -47,7 +49,7 @@ export abstract class Item extends React.Component<{ missing?: boolean; details?: string; }> { - render(): React.ReactNode { + override render(): React.ReactNode { const { selected, label, missing, details } = this.props; const classNames = ['item']; if (selected) { @@ -99,14 +101,18 @@ export class BoardsConfig extends React.Component< }; } - componentDidMount() { - this.updateBoards(); - this.updatePorts( - this.props.boardsServiceProvider.availableBoards - .map(({ port }) => port) - .filter(notEmpty) - ); + override componentDidMount(): void { this.toDispose.pushAll([ + this.props.onAppStateDidChange((state) => { + if (state === 'ready') { + this.updateBoards(); + this.updatePorts( + this.props.boardsServiceProvider.availableBoards + .map(({ port }) => port) + .filter(notEmpty) + ); + } + }), this.props.notificationCenter.onAttachedBoardsChanged((event) => this.updatePorts( event.newState.ports, @@ -141,11 +147,11 @@ export class BoardsConfig extends React.Component< ]); } - componentWillUnmount(): void { + override componentWillUnmount(): void { this.toDispose.dispose(); } - protected fireConfigChanged() { + protected fireConfigChanged(): void { const { selectedBoard, selectedPort } = this.state; this.props.onConfigChange({ selectedBoard, selectedPort }); } @@ -250,7 +256,7 @@ export class BoardsConfig extends React.Component< this.props.onFocusNodeSet(element || undefined); }; - render(): React.ReactNode { + override render(): React.ReactNode { return (
{this.renderContainer('boards', this.renderBoards.bind(this))} 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 5447427de..5f1c42e51 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 @@ -1,5 +1,5 @@ import * as PQueue from 'p-queue'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { @@ -13,6 +13,7 @@ import { BoardsDataStore } from './boards-data-store'; import { MainMenuManager } from '../../common/main-menu-manager'; import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus'; import { nls } from '@theia/core/lib/common'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; @injectable() export class BoardsDataMenuUpdater implements FrontendApplicationContribution { @@ -31,11 +32,20 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution { @inject(BoardsServiceProvider) protected readonly boardsServiceClient: BoardsServiceProvider; + @inject(FrontendApplicationStateService) + private readonly appStateService: FrontendApplicationStateService; + protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly toDisposeOnBoardChange = new DisposableCollection(); async onStart(): Promise { - this.updateMenuActions(this.boardsServiceClient.boardsConfig.selectedBoard); + this.appStateService + .reachedState('ready') + .then(() => + this.updateMenuActions( + this.boardsServiceClient.boardsConfig.selectedBoard + ) + ); this.boardsDataStore.onChanged(() => this.updateMenuActions( this.boardsServiceClient.boardsConfig.selectedBoard 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 d1a9de5d0..63255656a 100644 --- a/arduino-ide-extension/src/browser/boards/boards-data-store.ts +++ b/arduino-ide-extension/src/browser/boards/boards-data-store.ts @@ -1,4 +1,4 @@ -import { injectable, inject, named } from 'inversify'; +import { injectable, inject, named } from '@theia/core/shared/inversify'; import { ILogger } from '@theia/core/lib/common/logger'; import { deepClone } from '@theia/core/lib/common/objects'; import { Event, Emitter } from '@theia/core/lib/common/event'; 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 81749ea91..ca2508fb9 100644 --- a/arduino-ide-extension/src/browser/boards/boards-list-widget.ts +++ b/arduino-ide-extension/src/browser/boards/boards-list-widget.ts @@ -1,4 +1,4 @@ -import { inject, injectable, postConstruct } from 'inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { BoardsPackage, BoardsService, @@ -30,7 +30,7 @@ export class BoardsListWidget extends ListWidget { } @postConstruct() - protected init(): void { + protected override init(): void { super.init(); this.toDispose.pushAll([ this.notificationCenter.onPlatformInstalled(() => @@ -42,7 +42,7 @@ export class BoardsListWidget extends ListWidget { ]); } - protected async install({ + protected override async install({ item, progressId, version, @@ -63,7 +63,7 @@ export class BoardsListWidget extends ListWidget { ); } - protected async uninstall({ + protected override async uninstall({ item, progressId, }: { 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 190d5de3a..686e5e004 100644 --- a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts +++ b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts @@ -1,4 +1,4 @@ -import { injectable, inject } from 'inversify'; +import { injectable, inject } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { ILogger } from '@theia/core/lib/common/logger'; import { CommandService } from '@theia/core/lib/common/command'; 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 622e8fa50..cc7cd24da 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as React from '@theia/core/shared/react'; +import * as ReactDOM from '@theia/core/shared/react-dom'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { Port } from '../../common/protocol'; @@ -41,7 +41,7 @@ export class BoardsDropDown extends React.Component { } } - render(): React.ReactNode { + override render(): React.ReactNode { return ReactDOM.createPortal(this.renderNode(), this.dropdownElement); } @@ -130,13 +130,13 @@ export class BoardsToolBarItem extends React.Component< }); } - componentDidMount() { + override componentDidMount(): void { this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => this.setState({ availableBoards }) ); } - componentWillUnmount(): void { + override componentWillUnmount(): void { this.toDispose.dispose(); } @@ -161,7 +161,7 @@ export class BoardsToolBarItem extends React.Component< event.nativeEvent.stopImmediatePropagation(); }; - render(): React.ReactNode { + override render(): React.ReactNode { const { coords, availableBoards } = this.state; const boardsConfig = this.props.boardsServiceClient.boardsConfig; const title = BoardsConfig.Config.toString(boardsConfig, { 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 21aed8310..af31aff6e 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 @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { BoardsListWidget } from './boards-list-widget'; import { BoardsPackage } from '../../common/protocol/boards-service'; import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution'; @@ -18,7 +18,7 @@ export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendCont }); } - async initializeLayout(): Promise { + override async initializeLayout(): Promise { this.openView(); } } diff --git a/arduino-ide-extension/src/browser/components/ProgressBar.tsx b/arduino-ide-extension/src/browser/components/ProgressBar.tsx index f91c9f991..ea70508c3 100644 --- a/arduino-ide-extension/src/browser/components/ProgressBar.tsx +++ b/arduino-ide-extension/src/browser/components/ProgressBar.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; export type ProgressBarProps = { percent?: number; diff --git a/arduino-ide-extension/src/browser/contributions/about.ts b/arduino-ide-extension/src/browser/contributions/about.ts index 3f93adba2..f3a50fc54 100644 --- a/arduino-ide-extension/src/browser/contributions/about.ts +++ b/arduino-ide-extension/src/browser/contributions/about.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as moment from 'moment'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { isOSX, isWindows } from '@theia/core/lib/common/os'; @@ -22,13 +22,13 @@ export class About extends Contribution { @inject(ConfigService) protected readonly configService: ConfigService; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(About.Commands.ABOUT_APP, { execute: () => this.showAbout(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, { commandId: About.Commands.ABOUT_APP.id, label: nls.localize( diff --git a/arduino-ide-extension/src/browser/contributions/add-file.ts b/arduino-ide-extension/src/browser/contributions/add-file.ts index 94316a1f4..b7cb48f73 100644 --- a/arduino-ide-extension/src/browser/contributions/add-file.ts +++ b/arduino-ide-extension/src/browser/contributions/add-file.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { ArduinoMenus } from '../menu/arduino-menus'; import { @@ -10,19 +10,20 @@ import { } from './contribution'; import { FileDialogService } from '@theia/filesystem/lib/browser'; import { nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class AddFile extends SketchContribution { @inject(FileDialogService) protected readonly fileDialogService: FileDialogService; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(AddFile.Commands.ADD_FILE, { execute: () => this.addFile(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { commandId: AddFile.Commands.ADD_FILE.id, label: nls.localize('arduino/contributions/addFile', 'Add File') + '...', @@ -32,7 +33,7 @@ export class AddFile extends SketchContribution { protected async addFile(): Promise { const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } const toAddUri = await this.fileDialogService.showOpenDialog({ 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 9ae4c1a5e..927af4868 100644 --- a/arduino-ide-extension/src/browser/contributions/add-zip-library.ts +++ b/arduino-ide-extension/src/browser/contributions/add-zip-library.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import URI from '@theia/core/lib/common/uri'; import { ConfirmDialog } from '@theia/core/lib/browser/dialogs'; @@ -28,13 +28,13 @@ export class AddZipLibrary extends SketchContribution { @inject(LibraryService) protected readonly libraryService: LibraryService; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, { execute: () => this.addZipLibrary(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { const includeLibMenuPath = [ ...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include', diff --git a/arduino-ide-extension/src/browser/contributions/archive-sketch.ts b/arduino-ide-extension/src/browser/contributions/archive-sketch.ts index 2ab62dc22..abe22d77f 100644 --- a/arduino-ide-extension/src/browser/contributions/archive-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/archive-sketch.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as dateFormat from 'dateformat'; import URI from '@theia/core/lib/common/uri'; @@ -10,16 +10,17 @@ import { MenuModelRegistry, } from './contribution'; import { nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class ArchiveSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(ArchiveSketch.Commands.ARCHIVE_SKETCH, { execute: () => this.archiveSketch(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id, label: nls.localize('arduino/sketch/archiveSketch', 'Archive Sketch'), @@ -32,7 +33,7 @@ export class ArchiveSketch extends SketchContribution { this.sketchServiceClient.currentSketch(), this.configService.getConfiguration(), ]); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } const archiveBasename = `${sketch.name}-${dateFormat( diff --git a/arduino-ide-extension/src/browser/contributions/board-selection.ts b/arduino-ide-extension/src/browser/contributions/board-selection.ts index 9dc085fbf..16b025662 100644 --- a/arduino-ide-extension/src/browser/contributions/board-selection.ts +++ b/arduino-ide-extension/src/browser/contributions/board-selection.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { @@ -47,7 +47,7 @@ export class BoardSelection extends SketchContribution { protected readonly toDisposeBeforeMenuRebuild = new DisposableCollection(); - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, { execute: async () => { const { selectedBoard, selectedPort } = @@ -100,21 +100,22 @@ PID: ${PID}`; }); } - 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) + override onStart(): void { + this.notificationCenter.onPlatformInstalled(() => this.updateMenus()); + this.notificationCenter.onPlatformUninstalled(() => this.updateMenus()); + this.boardsServiceProvider.onBoardsConfigChanged(() => this.updateMenus()); + this.boardsServiceProvider.onAvailableBoardsChanged(() => + this.updateMenus() ); - this.boardsServiceProvider.onAvailablePortsChanged( - this.updateMenus.bind(this) + this.boardsServiceProvider.onAvailablePortsChanged(() => + this.updateMenus() ); } + override async onReady(): Promise { + this.updateMenus(); + } + protected async updateMenus(): Promise { const [installedBoards, availablePorts, config] = await Promise.all([ this.installedBoards(), diff --git a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts index 75aaef8fa..17cf91f30 100644 --- a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts +++ b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts @@ -1,9 +1,8 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { OutputChannelManager } from '@theia/output/lib/browser/output-channel'; import { CoreService } from '../../common/protocol'; import { ArduinoMenus } from '../menu/arduino-menus'; import { BoardsDataStore } from '../boards/boards-data-store'; -import { SerialConnectionManager } from '../serial/serial-connection-manager'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { SketchContribution, @@ -18,8 +17,6 @@ export class BurnBootloader extends SketchContribution { @inject(CoreService) protected readonly coreService: CoreService; - @inject(SerialConnectionManager) - protected readonly serialConnection: SerialConnectionManager; @inject(BoardsDataStore) protected readonly boardsDataStore: BoardsDataStore; @@ -28,15 +25,15 @@ export class BurnBootloader extends SketchContribution { protected readonly boardsServiceClientImpl: BoardsServiceProvider; @inject(OutputChannelManager) - protected readonly outputChannelManager: OutputChannelManager; + protected override readonly outputChannelManager: OutputChannelManager; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, { execute: () => this.burnBootloader(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, { commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id, label: nls.localize( @@ -60,9 +57,15 @@ export class BurnBootloader extends SketchContribution { this.preferences.get('arduino.upload.verify'), this.preferences.get('arduino.upload.verbose'), ]); + + const board = { + ...boardsConfig.selectedBoard, + name: boardsConfig.selectedBoard?.name || '', + fqbn, + } this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.burnBootloader({ - fqbn, + board, programmer, port, verify, @@ -85,8 +88,6 @@ export class BurnBootloader extends SketchContribution { errorMessage = e.toString(); } this.messageService.error(errorMessage); - } finally { - await this.serialConnection.reconnectAfterUpload(); } } } diff --git a/arduino-ide-extension/src/browser/contributions/close.ts b/arduino-ide-extension/src/browser/contributions/close.ts index 1b335fa82..45597613c 100644 --- a/arduino-ide-extension/src/browser/contributions/close.ts +++ b/arduino-ide-extension/src/browser/contributions/close.ts @@ -1,12 +1,10 @@ -import { inject, injectable } from 'inversify'; -import { toArray } from '@phosphor/algorithm'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { ArduinoMenus } from '../menu/arduino-menus'; -import { SaveAsSketch } from './save-as-sketch'; import { SketchContribution, Command, @@ -23,90 +21,21 @@ import { nls } from '@theia/core/lib/common'; @injectable() export class Close extends SketchContribution { @inject(EditorManager) - protected readonly editorManager: EditorManager; + protected override readonly editorManager: EditorManager; protected shell: ApplicationShell; - onStart(app: FrontendApplication): void { + override onStart(app: FrontendApplication): void { this.shell = app.shell; } - registerCommands(registry: CommandRegistry): void { + override 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) { - currentEditor.close(); - return; - } - - // 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) { - return currentWidgetInMain.close(); - } - } - - // Close the sketch (window). - const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { - return; - } - const isTemp = await this.sketchService.isTemp(sketch); - const uri = await this.sketchServiceClient.currentSketchFile(); - if (!uri) { - return; - } - if (isTemp && (await this.wasTouched(uri))) { - const { response } = await remote.dialog.showMessageBox({ - type: 'question', - buttons: [ - nls.localize( - 'vscode/abstractTaskService/saveBeforeRun.dontSave', - "Don't Save" - ), - nls.localize('vscode/issueMainService/cancel', 'Cancel'), - nls.localize( - 'vscode/abstractTaskService/saveBeforeRun.save', - 'Save' - ), - ], - message: nls.localize( - 'arduino/common/saveChangesToSketch', - 'Do you want to save changes to this sketch before closing?' - ), - detail: nls.localize( - 'arduino/common/loseChanges', - "If you don't save, your changes will be lost." - ), - }); - 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. - return; - } - } - } - window.close(); - }, + execute: () => remote.getCurrentWindow().close() }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: Close.Commands.CLOSE.id, label: nls.localize('vscode/editor.contribution/close', 'Close'), @@ -114,7 +43,7 @@ export class Close extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: Close.Commands.CLOSE.id, keybinding: 'CtrlCmd+W', diff --git a/arduino-ide-extension/src/browser/contributions/contribution.ts b/arduino-ide-extension/src/browser/contributions/contribution.ts index ee65185c0..1597cac28 100644 --- a/arduino-ide-extension/src/browser/contributions/contribution.ts +++ b/arduino-ide-extension/src/browser/contributions/contribution.ts @@ -1,4 +1,9 @@ -import { inject, injectable, interfaces } from 'inversify'; +import { + inject, + injectable, + interfaces, + postConstruct, +} from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { ILogger } from '@theia/core/lib/common/logger'; import { Saveable } from '@theia/core/lib/browser/saveable'; @@ -34,7 +39,10 @@ import { } from '@theia/core/lib/common/command'; import { EditorMode } from '../editor-mode'; import { SettingsService } from '../dialogs/settings/settings'; -import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl'; +import { + CurrentSketch, + SketchesServiceClientImpl, +} from '../../common/protocol/sketches-service-client-impl'; import { SketchesService, ConfigService, @@ -42,6 +50,7 @@ import { Sketch, } from '../../common/protocol'; import { ArduinoPreferences } from '../arduino-preferences'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; export { Command, @@ -84,15 +93,31 @@ export abstract class Contribution @inject(SettingsService) protected readonly settingsService: SettingsService; + @inject(FrontendApplicationStateService) + protected readonly appStateService: FrontendApplicationStateService; + + @postConstruct() + protected init(): void { + this.appStateService.reachedState('ready').then(() => this.onReady()); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars onStart(app: FrontendApplication): MaybePromise {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars registerCommands(registry: CommandRegistry): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars registerMenus(registry: MenuModelRegistry): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars registerKeybindings(registry: KeybindingRegistry): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars registerToolbarItems(registry: TabBarToolbarRegistry): void {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + onReady(): MaybePromise {} } @injectable() @@ -127,7 +152,7 @@ export abstract class SketchContribution extends Contribution { protected async sourceOverride(): Promise> { const override: Record = {}; const sketch = await this.sketchServiceClient.currentSketch(); - if (sketch) { + if (CurrentSketch.isValid(sketch)) { for (const editor of this.editorManager.all) { const uri = editor.editor.uri; if (Saveable.isDirty(editor) && Sketch.isInSketch(uri, sketch)) { @@ -140,7 +165,7 @@ export abstract class SketchContribution extends Contribution { } export namespace Contribution { - export function configure( + export function configure( bind: interfaces.Bind, serviceIdentifier: typeof Contribution ): void { diff --git a/arduino-ide-extension/src/browser/contributions/debug.ts b/arduino-ide-extension/src/browser/contributions/debug.ts index a67f832e9..b1550ce32 100644 --- a/arduino-ide-extension/src/browser/contributions/debug.ts +++ b/arduino-ide-extension/src/browser/contributions/debug.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; @@ -12,7 +12,8 @@ import { SketchContribution, TabBarToolbarRegistry, } from './contribution'; -import { nls } from '@theia/core/lib/common'; +import { MaybePromise, nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class Debug extends SketchContribution { @@ -66,7 +67,7 @@ export class Debug extends SketchContribution { onDidChange: this.onDisabledMessageDidChange as Event, }; - onStart(): void { + override onStart(): void { this.onDisabledMessageDidChange( () => (this.debugToolbarItem.tooltip = `${ @@ -79,55 +80,18 @@ export class Debug extends SketchContribution { : Debug.Commands.START_DEBUGGING.label }`) ); - const refreshState = async ( - board: Board | undefined = this.boardsServiceProvider.boardsConfig - .selectedBoard - ) => { - if (!board) { - this.disabledMessage = nls.localize( - 'arduino/common/noBoardSelected', - 'No board selected' - ); - return; - } - const fqbn = board.fqbn; - if (!fqbn) { - this.disabledMessage = nls.localize( - 'arduino/debug/noPlatformInstalledFor', - "Platform is not installed for '{0}'", - board.name - ); - return; - } - const details = await this.boardService.getBoardDetails({ fqbn }); - if (!details) { - this.disabledMessage = nls.localize( - 'arduino/debug/noPlatformInstalledFor', - "Platform is not installed for '{0}'", - board.name - ); - return; - } - const { debuggingSupported } = details; - if (!debuggingSupported) { - this.disabledMessage = nls.localize( - 'arduino/debug/debuggingNotSupported', - "Debugging is not supported by '{0}'", - board.name - ); - } else { - this.disabledMessage = undefined; - } - }; this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) => - refreshState(selectedBoard) + this.refreshState(selectedBoard) ); - this.notificationCenter.onPlatformInstalled(() => refreshState()); - this.notificationCenter.onPlatformUninstalled(() => refreshState()); - refreshState(); + this.notificationCenter.onPlatformInstalled(() => this.refreshState()); + this.notificationCenter.onPlatformUninstalled(() => this.refreshState()); + } + + override onReady(): MaybePromise { + this.refreshState(); } - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(Debug.Commands.START_DEBUGGING, { execute: () => this.startDebug(), isVisible: (widget) => @@ -136,10 +100,51 @@ export class Debug extends SketchContribution { }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem(this.debugToolbarItem); } + private async refreshState( + board: Board | undefined = this.boardsServiceProvider.boardsConfig + .selectedBoard + ): Promise { + if (!board) { + this.disabledMessage = nls.localize( + 'arduino/common/noBoardSelected', + 'No board selected' + ); + return; + } + const fqbn = board.fqbn; + if (!fqbn) { + this.disabledMessage = nls.localize( + 'arduino/debug/noPlatformInstalledFor', + "Platform is not installed for '{0}'", + board.name + ); + return; + } + const details = await this.boardService.getBoardDetails({ fqbn }); + if (!details) { + this.disabledMessage = nls.localize( + 'arduino/debug/noPlatformInstalledFor', + "Platform is not installed for '{0}'", + board.name + ); + return; + } + const { debuggingSupported } = details; + if (!debuggingSupported) { + this.disabledMessage = nls.localize( + 'arduino/debug/debuggingNotSupported', + "Debugging is not supported by '{0}'", + board.name + ); + } else { + this.disabledMessage = undefined; + } + } + protected async startDebug( board: Board | undefined = this.boardsServiceProvider.boardsConfig .selectedBoard @@ -156,7 +161,7 @@ export class Debug extends SketchContribution { this.sketchServiceClient.currentSketch(), this.executableService.list(), ]); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri( diff --git a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts index 9d6e95234..6b77d5163 100644 --- a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts +++ b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; @@ -12,6 +12,8 @@ import { } from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { nls } from '@theia/core/lib/common'; +import type { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; +import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; // 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 @@ -26,7 +28,7 @@ export class EditContributions extends Contribution { @inject(PreferenceService) protected readonly preferences: PreferenceService; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(EditContributions.Commands.GO_TO_LINE, { execute: () => this.run('editor.action.gotoLine'), }); @@ -91,7 +93,7 @@ ${value} }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { commandId: CommonCommands.CUT.id, order: '0', @@ -199,7 +201,7 @@ ${value} }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: EditContributions.Commands.COPY_FOR_FORUM.id, keybinding: 'CtrlCmd+Shift+C', @@ -250,10 +252,10 @@ ${value} }); } - protected async current(): Promise { + protected async current(): Promise { return ( this.codeEditorService.getFocusedCodeEditor() || - this.codeEditorService.getActiveCodeEditor() + this.codeEditorService.getActiveCodeEditor() || undefined ); } diff --git a/arduino-ide-extension/src/browser/contributions/examples.ts b/arduino-ide-extension/src/browser/contributions/examples.ts index 9fd045c3b..17368feab 100644 --- a/arduino-ide-extension/src/browser/contributions/examples.ts +++ b/arduino-ide-extension/src/browser/contributions/examples.ts @@ -1,5 +1,5 @@ import * as PQueue from 'p-queue'; -import { inject, injectable, postConstruct } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CommandHandler } from '@theia/core/lib/common/command'; import { MenuPath, @@ -21,7 +21,7 @@ import { MenuModelRegistry, } from './contribution'; import { NotificationCenter } from '../notification-center'; -import { Board, Sketch, SketchContainer } from '../../common/protocol'; +import { Board, SketchRef, SketchContainer } from '../../common/protocol'; import { nls } from '@theia/core/lib/common'; @injectable() @@ -43,8 +43,8 @@ export abstract class Examples extends SketchContribution { protected readonly toDispose = new DisposableCollection(); - @postConstruct() - init(): void { + protected override init(): void { + super.init(); this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.handleBoardChanged(selectedBoard) ); @@ -54,7 +54,7 @@ export abstract class Examples extends SketchContribution { // NOOP } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { try { // This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222. const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1; @@ -82,7 +82,7 @@ export abstract class Examples extends SketchContribution { registerRecursively( sketchContainerOrPlaceholder: | SketchContainer - | (Sketch | SketchContainer)[] + | (SketchRef | SketchContainer)[] | string, menuPath: MenuPath, pushToDispose: DisposableCollection = new DisposableCollection(), @@ -100,7 +100,7 @@ export abstract class Examples extends SketchContribution { ) ); } else { - const sketches: Sketch[] = []; + const sketches: SketchRef[] = []; const children: SketchContainer[] = []; let submenuPath = menuPath; @@ -161,7 +161,7 @@ export abstract class Examples extends SketchContribution { @injectable() export class BuiltInExamples extends Examples { - onStart(): void { + override async onReady(): Promise { this.register(); // no `await` } @@ -201,13 +201,16 @@ export class LibraryExamples extends Examples { protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); - onStart(): void { - this.register(); // no `await` + override onStart(): void { this.notificationCenter.onLibraryInstalled(() => this.register()); this.notificationCenter.onLibraryUninstalled(() => this.register()); } - protected handleBoardChanged(board: Board | undefined): void { + override async onReady(): Promise { + this.register(); // no `await` + } + + protected override handleBoardChanged(board: Board | undefined): void { this.register(board); } diff --git a/arduino-ide-extension/src/browser/contributions/format.ts b/arduino-ide-extension/src/browser/contributions/format.ts new file mode 100644 index 000000000..17f1edf0a --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/format.ts @@ -0,0 +1,94 @@ +import { MaybePromise } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import * as monaco from '@theia/monaco-editor-core'; +import { Formatter } from '../../common/protocol/formatter'; +import { Contribution, URI } from './contribution'; + +@injectable() +export class Format + extends Contribution + implements + monaco.languages.DocumentRangeFormattingEditProvider, + monaco.languages.DocumentFormattingEditProvider +{ + @inject(Formatter) + private readonly formatter: Formatter; + + override onStart(): MaybePromise { + const selector = this.selectorOf('ino', 'c', 'cpp', 'h', 'hpp', 'pde'); + monaco.languages.registerDocumentRangeFormattingEditProvider( + selector, + this + ); + monaco.languages.registerDocumentFormattingEditProvider(selector, this); + } + async provideDocumentRangeFormattingEdits( + model: monaco.editor.ITextModel, + range: monaco.Range, + options: monaco.languages.FormattingOptions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: monaco.CancellationToken + ): Promise { + const text = await this.format(model, range, options); + return [{ range, text }]; + } + + async provideDocumentFormattingEdits( + model: monaco.editor.ITextModel, + options: monaco.languages.FormattingOptions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: monaco.CancellationToken + ): Promise { + const range = this.fullRange(model); + const text = await this.format(model, range, options); + return [{ range, text }]; + } + + private fullRange(model: monaco.editor.ITextModel): monaco.Range { + const lastLine = model.getLineCount(); + const lastLineMaxColumn = model.getLineMaxColumn(lastLine); + const end = new monaco.Position(lastLine, lastLineMaxColumn); + return monaco.Range.fromPositions(new monaco.Position(1, 1), end); + } + + /** + * From the currently opened workspaces (IDE2 has always one), it calculates all possible + * folder locations where the `.clang-format` file could be. + */ + private formatterConfigFolderUris(model: monaco.editor.ITextModel): string[] { + const editorUri = new URI(model.uri.toString()); + return this.workspaceService + .tryGetRoots() + .map(({ resource }) => resource) + .filter((workspaceUri) => workspaceUri.isEqualOrParent(editorUri)) + .map((uri) => uri.toString()); + } + + private format( + model: monaco.editor.ITextModel, + range: monaco.Range, + options: monaco.languages.FormattingOptions + ): Promise { + console.info( + `Formatting ${model.uri.toString()} [Range: ${JSON.stringify( + range.toJSON() + )}]` + ); + const content = model.getValueInRange(range); + const formatterConfigFolderUris = this.formatterConfigFolderUris(model); + return this.formatter.format({ + content, + formatterConfigFolderUris, + options, + }); + } + + private selectorOf( + ...languageId: string[] + ): monaco.languages.LanguageSelector { + return languageId.map((language) => ({ + language, + exclusive: true, // <-- this should make sure the custom formatter has higher precedence over the LS formatter. + })); + } +} diff --git a/arduino-ide-extension/src/browser/contributions/help.ts b/arduino-ide-extension/src/browser/contributions/help.ts index 23594d331..36e09f52e 100644 --- a/arduino-ide-extension/src/browser/contributions/help.ts +++ b/arduino-ide-extension/src/browser/contributions/help.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; @@ -15,6 +15,7 @@ import { import { nls } from '@theia/core/lib/common'; import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands'; import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution'; +import * as monaco from '@theia/monaco-editor-core'; @injectable() export class Help extends Contribution { @@ -27,7 +28,7 @@ export class Help extends Contribution { @inject(QuickInputService) protected readonly quickInputService: QuickInputService; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { const open = (url: string) => this.windowService.openNewWindow(url, { external: true }); const createOpenHandler = (url: string) => @@ -91,7 +92,7 @@ export class Help extends Contribution { ); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.unregisterMenuAction({ commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id, }); @@ -135,7 +136,7 @@ export class Help extends Contribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: Help.Commands.FIND_IN_REFERENCE.id, keybinding: 'CtrlCmd+Shift+F', diff --git a/arduino-ide-extension/src/browser/contributions/include-library.ts b/arduino-ide-extension/src/browser/contributions/include-library.ts index 77f6bcf39..7347c3fa9 100644 --- a/arduino-ide-extension/src/browser/contributions/include-library.ts +++ b/arduino-ide-extension/src/browser/contributions/include-library.ts @@ -1,5 +1,5 @@ import * as PQueue from 'p-queue'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { EditorManager } from '@theia/editor/lib/browser'; @@ -16,6 +16,8 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { SketchContribution, Command, CommandRegistry } from './contribution'; import { NotificationCenter } from '../notification-center'; import { nls } from '@theia/core/lib/common'; +import * as monaco from '@theia/monaco-editor-core'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class IncludeLibrary extends SketchContribution { @@ -29,7 +31,7 @@ export class IncludeLibrary extends SketchContribution { protected readonly mainMenuManager: MainMenuManager; @inject(EditorManager) - protected readonly editorManager: EditorManager; + protected override readonly editorManager: EditorManager; @inject(NotificationCenter) protected readonly notificationCenter: NotificationCenter; @@ -43,8 +45,7 @@ export class IncludeLibrary extends SketchContribution { protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly toDispose = new DisposableCollection(); - onStart(): void { - this.updateMenuActions(); + override onStart(): void { this.boardsServiceClient.onBoardsConfigChanged(() => this.updateMenuActions() ); @@ -54,7 +55,11 @@ export class IncludeLibrary extends SketchContribution { ); } - registerMenus(registry: MenuModelRegistry): void { + override async onReady(): Promise { + this.updateMenuActions(); + } + + override registerMenus(registry: MenuModelRegistry): void { // `Include Library` submenu const includeLibMenuPath = [ ...ArduinoMenus.SKETCH__UTILS_GROUP, @@ -77,7 +82,7 @@ export class IncludeLibrary extends SketchContribution { }); } - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, { execute: async (arg) => { if (LibraryPackage.is(arg)) { @@ -168,7 +173,7 @@ export class IncludeLibrary extends SketchContribution { protected async includeLibrary(library: LibraryPackage): Promise { const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } // If the current editor is one of the additional files from the sketch, we use that. diff --git a/arduino-ide-extension/src/browser/contributions/new-sketch.ts b/arduino-ide-extension/src/browser/contributions/new-sketch.ts index fa4e6686e..685ae7e2b 100644 --- a/arduino-ide-extension/src/browser/contributions/new-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/new-sketch.ts @@ -1,5 +1,5 @@ import { nls } from '@theia/core/lib/common'; -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { @@ -14,7 +14,7 @@ import { @injectable() export class NewSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(NewSketch.Commands.NEW_SKETCH, { execute: () => this.newSketch(), }); @@ -25,7 +25,7 @@ export class NewSketch extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: NewSketch.Commands.NEW_SKETCH.id, label: nls.localize('arduino/sketch/new', 'New'), @@ -33,14 +33,14 @@ export class NewSketch extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: NewSketch.Commands.NEW_SKETCH.id, keybinding: 'CtrlCmd+N', }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, 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 468aef749..dfedf5d8c 100644 --- a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol'; import { Disposable, @@ -35,18 +35,19 @@ export class OpenRecentSketch extends SketchContribution { protected toDisposeBeforeRegister = new Map(); - onStart(): void { - const refreshMenu = (sketches: Sketch[]) => { - this.register(sketches); - this.mainMenuManager.update(); - }; + override onStart(): void { this.notificationCenter.onRecentSketchesChanged(({ sketches }) => - refreshMenu(sketches) + this.refreshMenu(sketches) ); - this.sketchService.recentlyOpenedSketches().then(refreshMenu); } - registerMenus(registry: MenuModelRegistry): void { + override async onReady(): Promise { + this.sketchService + .recentlyOpenedSketches() + .then((sketches) => this.refreshMenu(sketches)); + } + + override registerMenus(registry: MenuModelRegistry): void { registry.registerSubmenu( ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, nls.localize('arduino/sketch/openRecent', 'Open Recent'), @@ -54,6 +55,11 @@ export class OpenRecentSketch extends SketchContribution { ); } + private refreshMenu(sketches: Sketch[]): void { + this.register(sketches); + this.mainMenuManager.update(); + } + protected register(sketches: Sketch[]): void { const order = 0; for (const sketch of sketches) { 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 976902588..03207126f 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch-external.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import URI from '@theia/core/lib/common/uri'; import { ArduinoMenus } from '../menu/arduino-menus'; @@ -13,13 +13,13 @@ import { nls } from '@theia/core/lib/common'; @injectable() export class OpenSketchExternal extends SketchContribution { - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(OpenSketchExternal.Commands.OPEN_EXTERNAL, { execute: () => this.openExternal(), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, label: nls.localize('arduino/sketch/showFolder', 'Show Sketch Folder'), @@ -27,7 +27,7 @@ export class OpenSketchExternal extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, keybinding: 'CtrlCmd+Alt+K', diff --git a/arduino-ide-extension/src/browser/contributions/open-sketch.ts b/arduino-ide-extension/src/browser/contributions/open-sketch.ts index 879ab144f..f110addc3 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { MaybePromise } from '@theia/core/lib/common/types'; import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser'; @@ -43,7 +43,7 @@ export class OpenSketch extends SketchContribution { protected readonly toDispose = new DisposableCollection(); - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH, { execute: (arg) => Sketch.is(arg) ? this.openSketch(arg) : this.openSketch(), @@ -116,7 +116,7 @@ export class OpenSketch extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: OpenSketch.Commands.OPEN_SKETCH.id, label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'), @@ -124,14 +124,14 @@ export class OpenSketch extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: OpenSketch.Commands.OPEN_SKETCH.id, keybinding: 'CtrlCmd+O', }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, diff --git a/arduino-ide-extension/src/browser/contributions/quit-app.ts b/arduino-ide-extension/src/browser/contributions/quit-app.ts index c0e784726..17a7874dd 100644 --- a/arduino-ide-extension/src/browser/contributions/quit-app.ts +++ b/arduino-ide-extension/src/browser/contributions/quit-app.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { isOSX } from '@theia/core/lib/common/os'; import { @@ -13,7 +13,7 @@ import { nls } from '@theia/core/lib/common'; @injectable() export class QuitApp extends Contribution { - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { if (!isOSX) { registry.registerCommand(QuitApp.Commands.QUIT_APP, { execute: () => remote.app.quit(), @@ -21,7 +21,7 @@ export class QuitApp extends Contribution { } } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { // 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, { @@ -32,7 +32,7 @@ export class QuitApp extends Contribution { } } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { if (!isOSX) { registry.registerKeybinding({ command: QuitApp.Commands.QUIT_APP.id, 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 0c265d0c2..6aa63f30e 100644 --- a/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/save-as-sketch.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as dateFormat from 'dateformat'; import { ArduinoMenus } from '../menu/arduino-menus'; @@ -11,16 +11,30 @@ import { KeybindingRegistry, } from './contribution'; import { nls } from '@theia/core/lib/common'; +import { ApplicationShell, NavigatableWidget, Saveable } from '@theia/core/lib/browser'; +import { EditorManager } from '@theia/editor/lib/browser'; +import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class SaveAsSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { + + @inject(ApplicationShell) + protected readonly applicationShell: ApplicationShell; + + @inject(EditorManager) + protected override readonly editorManager: EditorManager; + + @inject(WindowService) + protected readonly windowService: WindowService; + + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, { execute: (args) => this.saveAs(args), }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'), @@ -28,7 +42,7 @@ export class SaveAsSketch extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, keybinding: 'CtrlCmd+Shift+S', @@ -46,7 +60,7 @@ export class SaveAsSketch extends SketchContribution { }: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT ): Promise { const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return false; } @@ -90,6 +104,9 @@ export class SaveAsSketch extends SketchContribution { const workspaceUri = await this.sketchService.copy(sketch, { destinationUri, }); + if (workspaceUri) { + await this.saveOntoCopiedSketch(sketch.mainFileUri, sketch.uri, workspaceUri); + } if (workspaceUri && openAfterMove) { if (wipeOriginal || (openAfterMove && execOnlyIfTemp)) { try { @@ -100,12 +117,48 @@ export class SaveAsSketch extends SketchContribution { /* NOOP: from time to time, it's not possible to wipe the old resource from the temp dir on Windows */ } } + this.windowService.setSafeToShutDown(); this.workspaceService.open(new URI(workspaceUri), { preserveWindow: true, }); } return !!workspaceUri; } + + private async saveOntoCopiedSketch(mainFileUri: string, sketchUri: string, newSketchUri: string): Promise { + const widgets = this.applicationShell.widgets; + const snapshots = new Map(); + for (const widget of widgets) { + const saveable = Saveable.getDirty(widget); + const uri = NavigatableWidget.getUri(widget); + const uriString = uri?.toString(); + let relativePath: string; + if (uri && uriString!.includes(sketchUri) && saveable && saveable.createSnapshot) { + // The main file will change its name during the copy process + // We need to store the new name in the map + if (mainFileUri === uriString) { + const lastPart = new URI(newSketchUri).path.base + uri.path.ext; + relativePath = '/' + lastPart; + } else { + relativePath = uri.toString().substring(sketchUri.length); + } + snapshots.set(relativePath, saveable.createSnapshot()); + } + } + await Promise.all(Array.from(snapshots.entries()).map(async ([path, snapshot]) => { + const widgetUri = new URI(newSketchUri + path); + try { + const widget = await this.editorManager.getOrCreateByUri(widgetUri); + const saveable = Saveable.get(widget); + if (saveable && saveable.applySnapshot) { + saveable.applySnapshot(snapshot); + await saveable.save(); + } + } catch (e) { + console.error(e); + } + })); + } } export namespace SaveAsSketch { diff --git a/arduino-ide-extension/src/browser/contributions/save-sketch.ts b/arduino-ide-extension/src/browser/contributions/save-sketch.ts index 5dddcffa1..2c1ab550e 100644 --- a/arduino-ide-extension/src/browser/contributions/save-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/save-sketch.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; @@ -12,10 +12,11 @@ import { TabBarToolbarRegistry, } from './contribution'; import { nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class SaveSketch extends SketchContribution { - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH, { execute: () => this.saveSketch(), }); @@ -27,7 +28,7 @@ export class SaveSketch extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { commandId: SaveSketch.Commands.SAVE_SKETCH.id, label: nls.localize('vscode/fileCommands/save', 'Save'), @@ -35,14 +36,14 @@ export class SaveSketch extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: SaveSketch.Commands.SAVE_SKETCH.id, keybinding: 'CtrlCmd+S', }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, @@ -53,7 +54,7 @@ export class SaveSketch extends SketchContribution { async saveSketch(): Promise { const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } const isTemp = await this.sketchService.isTemp(sketch); diff --git a/arduino-ide-extension/src/browser/contributions/settings.ts b/arduino-ide-extension/src/browser/contributions/settings.ts index 912b33eb8..32030809e 100644 --- a/arduino-ide-extension/src/browser/contributions/settings.ts +++ b/arduino-ide-extension/src/browser/contributions/settings.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Command, MenuModelRegistry, @@ -18,7 +18,7 @@ export class Settings extends SketchContribution { protected settingsOpened = false; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(Settings.Commands.OPEN, { execute: async () => { let settings: Preferences | undefined = undefined; @@ -39,7 +39,7 @@ export class Settings extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, { commandId: Settings.Commands.OPEN.id, label: @@ -52,7 +52,7 @@ export class Settings extends SketchContribution { registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced'); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: Settings.Commands.OPEN.id, keybinding: 'CtrlCmd+,', diff --git a/arduino-ide-extension/src/browser/contributions/sketch-control.ts b/arduino-ide-extension/src/browser/contributions/sketch-control.ts index 38bc33fe3..ea376fea1 100644 --- a/arduino-ide-extension/src/browser/contributions/sketch-control.ts +++ b/arduino-ide-extension/src/browser/contributions/sketch-control.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { WorkspaceCommands } from '@theia/workspace/lib/browser'; @@ -19,7 +19,10 @@ import { } from './contribution'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; -import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl'; +import { + CurrentSketch, + SketchesServiceClientImpl, +} from '../../common/protocol/sketches-service-client-impl'; import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider'; import { nls } from '@theia/core/lib/common'; @@ -35,7 +38,7 @@ export class SketchControl extends SketchContribution { protected readonly contextMenuRenderer: ContextMenuRenderer; @inject(EditorManager) - protected readonly editorManager: EditorManager; + protected override readonly editorManager: EditorManager; @inject(SketchesServiceClientImpl) protected readonly sketchesServiceClient: SketchesServiceClientImpl; @@ -46,7 +49,7 @@ export class SketchControl extends SketchContribution { protected readonly toDisposeBeforeCreateNewContextMenu = new DisposableCollection(); - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand( SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR, { @@ -55,7 +58,7 @@ export class SketchControl extends SketchContribution { execute: async () => { this.toDisposeBeforeCreateNewContextMenu.dispose(); const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } @@ -70,25 +73,22 @@ export class SketchControl extends SketchContribution { return; } - const { mainFileUri, rootFolderFileUris } = - await this.sketchService.loadSketch(sketch.uri); + const { mainFileUri, rootFolderFileUris } = sketch; const uris = [mainFileUri, ...rootFolderFileUris]; - const currentSketch = - await this.sketchesServiceClient.currentSketch(); - const parentsketchUri = this.editorManager.currentEditor + const parentSketchUri = this.editorManager.currentEditor ?.getResourceUri() ?.toString(); - const parentsketch = await this.sketchService.getSketchFolder( - parentsketchUri || '' + const parentSketch = await this.sketchService.getSketchFolder( + parentSketchUri || '' ); // if the current file is in the current opened sketch, show extra menus if ( - currentSketch && - parentsketch && - parentsketch.uri === currentSketch.uri && - this.allowRename(parentsketch.uri) + sketch && + parentSketch && + parentSketch.uri === sketch.uri && + this.allowRename(parentSketch.uri) ) { this.menuRegistry.registerMenuAction( ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, @@ -122,10 +122,10 @@ export class SketchControl extends SketchContribution { } if ( - currentSketch && - parentsketch && - parentsketch.uri === currentSketch.uri && - this.allowDelete(parentsketch.uri) + sketch && + parentSketch && + parentSketch.uri === sketch.uri && + this.allowDelete(parentSketch.uri) ) { this.menuRegistry.registerMenuAction( ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, @@ -200,7 +200,7 @@ export class SketchControl extends SketchContribution { ); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction( ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, { @@ -228,7 +228,7 @@ export class SketchControl extends SketchContribution { ); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: WorkspaceCommands.NEW_FILE.id, keybinding: 'CtrlCmd+Shift+N', @@ -243,7 +243,7 @@ export class SketchControl extends SketchContribution { }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, diff --git a/arduino-ide-extension/src/browser/contributions/sketchbook.ts b/arduino-ide-extension/src/browser/contributions/sketchbook.ts index a5f691974..80dc99065 100644 --- a/arduino-ide-extension/src/browser/contributions/sketchbook.ts +++ b/arduino-ide-extension/src/browser/contributions/sketchbook.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CommandHandler } from '@theia/core/lib/common/command'; import { CommandRegistry, MenuModelRegistry } from './contribution'; import { ArduinoMenus } from '../menu/arduino-menus'; @@ -12,10 +12,10 @@ import { nls } from '@theia/core/lib/common'; @injectable() export class Sketchbook extends Examples { @inject(CommandRegistry) - protected readonly commandRegistry: CommandRegistry; + protected override readonly commandRegistry: CommandRegistry; @inject(MenuModelRegistry) - protected readonly menuRegistry: MenuModelRegistry; + protected override readonly menuRegistry: MenuModelRegistry; @inject(MainMenuManager) protected readonly mainMenuManager: MainMenuManager; @@ -23,11 +23,7 @@ export class Sketchbook extends Examples { @inject(NotificationCenter) protected readonly notificationCenter: NotificationCenter; - onStart(): void { - this.sketchService.getSketches({}).then((container) => { - this.register(container); - this.mainMenuManager.update(); - }); + override onStart(): void { this.sketchServiceClient.onSketchbookDidChange(() => { this.sketchService.getSketches({}).then((container) => { this.register(container); @@ -36,7 +32,14 @@ export class Sketchbook extends Examples { }); } - registerMenus(registry: MenuModelRegistry): void { + override async onReady(): Promise { + this.sketchService.getSketches({}).then((container) => { + this.register(container); + this.mainMenuManager.update(); + }); + } + + override registerMenus(registry: MenuModelRegistry): void { registry.registerSubmenu( ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, nls.localize('arduino/sketch/sketchbook', 'Sketchbook'), @@ -53,7 +56,7 @@ export class Sketchbook extends Examples { ); } - protected createHandler(uri: string): CommandHandler { + protected override createHandler(uri: string): CommandHandler { return { execute: async () => { const sketch = await this.sketchService.loadSketch(uri); diff --git a/arduino-ide-extension/src/browser/contributions/upload-certificate.ts b/arduino-ide-extension/src/browser/contributions/upload-certificate.ts index 0a4f9be39..91292eb49 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-certificate.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-certificate.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Command, MenuModelRegistry, @@ -39,7 +39,7 @@ export class UploadCertificate extends Contribution { protected dialogOpened = false; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(UploadCertificate.Commands.OPEN, { execute: async () => { try { @@ -93,7 +93,7 @@ export class UploadCertificate extends Contribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, { commandId: UploadCertificate.Commands.OPEN.id, label: UploadCertificate.Commands.OPEN.label, diff --git a/arduino-ide-extension/src/browser/contributions/upload-firmware.ts b/arduino-ide-extension/src/browser/contributions/upload-firmware.ts index 43af8b14f..6fc566904 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-firmware.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-firmware.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Command, MenuModelRegistry, @@ -16,7 +16,7 @@ export class UploadFirmware extends Contribution { protected dialogOpened = false; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(UploadFirmware.Commands.OPEN, { execute: async () => { try { @@ -30,7 +30,7 @@ export class UploadFirmware extends Contribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, { commandId: UploadFirmware.Commands.OPEN.id, label: UploadFirmware.Commands.OPEN.label, diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index df196cb7f..76a8f4973 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -1,10 +1,9 @@ -import { inject, injectable, postConstruct } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { BoardUserField, CoreService } from '../../common/protocol'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { BoardsDataStore } from '../boards/boards-data-store'; -import { SerialConnectionManager } from '../serial/serial-connection-manager'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { SketchContribution, @@ -16,15 +15,13 @@ import { } from './contribution'; import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog'; import { DisposableCollection, nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class UploadSketch extends SketchContribution { @inject(CoreService) protected readonly coreService: CoreService; - @inject(SerialConnectionManager) - protected readonly serialConnection: SerialConnectionManager; - @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry; @@ -47,8 +44,8 @@ export class UploadSketch extends SketchContribution { protected readonly menuActionsDisposables = new DisposableCollection(); - @postConstruct() - protected init(): void { + protected override init(): void { + super.init(); this.boardsServiceClientImpl.onBoardsConfigChanged(async () => { const userFields = await this.boardsServiceClientImpl.selectedBoardUserFields(); @@ -72,7 +69,7 @@ export class UploadSketch extends SketchContribution { return fqbn + '|' + address; } - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, { execute: async () => { const key = this.selectedFqbnAddress(); @@ -134,7 +131,7 @@ export class UploadSketch extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { this.menuActionsDisposables.dispose(); this.menuActionsDisposables.push( @@ -177,7 +174,7 @@ export class UploadSketch extends SketchContribution { ); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: UploadSketch.Commands.UPLOAD_SKETCH.id, keybinding: 'CtrlCmd+U', @@ -188,7 +185,7 @@ export class UploadSketch extends SketchContribution { }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id, command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id, @@ -209,7 +206,7 @@ export class UploadSketch extends SketchContribution { this.uploadInProgress = true; this.onDidChangeEmitter.fire(); const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } @@ -226,6 +223,11 @@ export class UploadSketch extends SketchContribution { this.sourceOverride(), ]); + const board = { + ...boardsConfig.selectedBoard, + name: boardsConfig.selectedBoard?.name || '', + fqbn, + } let options: CoreService.Upload.Options | undefined = undefined; const sketchUri = sketch.uri; const optimizeForDebug = this.editorMode.compileForDebug; @@ -247,7 +249,7 @@ export class UploadSketch extends SketchContribution { const programmer = selectedProgrammer; options = { sketchUri, - fqbn, + board, optimizeForDebug, programmer, port, @@ -259,7 +261,7 @@ export class UploadSketch extends SketchContribution { } else { options = { sketchUri, - fqbn, + board, optimizeForDebug, port, verbose, @@ -289,8 +291,6 @@ export class UploadSketch extends SketchContribution { } finally { this.uploadInProgress = false; this.onDidChangeEmitter.fire(); - - setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000); } } } diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index 898953ae8..fdc5b504f 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { CoreService } from '../../common/protocol'; import { ArduinoMenus } from '../menu/arduino-menus'; @@ -14,6 +14,7 @@ import { TabBarToolbarRegistry, } from './contribution'; import { nls } from '@theia/core/lib/common'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; @injectable() export class VerifySketch extends SketchContribution { @@ -31,7 +32,7 @@ export class VerifySketch extends SketchContribution { protected verifyInProgress = false; - registerCommands(registry: CommandRegistry): void { + override registerCommands(registry: CommandRegistry): void { registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, { execute: () => this.verifySketch(), isEnabled: () => !this.verifyInProgress, @@ -50,7 +51,7 @@ export class VerifySketch extends SketchContribution { }); } - registerMenus(registry: MenuModelRegistry): void { + override registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { commandId: VerifySketch.Commands.VERIFY_SKETCH.id, label: nls.localize('arduino/sketch/verifyOrCompile', 'Verify/Compile'), @@ -66,7 +67,7 @@ export class VerifySketch extends SketchContribution { }); } - registerKeybindings(registry: KeybindingRegistry): void { + override registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ command: VerifySketch.Commands.VERIFY_SKETCH.id, keybinding: 'CtrlCmd+R', @@ -77,7 +78,7 @@ export class VerifySketch extends SketchContribution { }); } - registerToolbarItems(registry: TabBarToolbarRegistry): void { + override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id, command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id, @@ -99,7 +100,7 @@ export class VerifySketch extends SketchContribution { this.onDidChangeEmitter.fire(); const sketch = await this.sketchServiceClient.currentSketch(); - if (!sketch) { + if (!CurrentSketch.isValid(sketch)) { return; } try { @@ -110,12 +111,17 @@ export class VerifySketch extends SketchContribution { ), this.sourceOverride(), ]); + const board = { + ...boardsConfig.selectedBoard, + name: boardsConfig.selectedBoard?.name || '', + fqbn, + } const verbose = this.preferences.get('arduino.compile.verbose'); const compilerWarnings = this.preferences.get('arduino.compile.warnings'); this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.compile({ sketchUri: sketch.uri, - fqbn, + board, optimizeForDebug: this.editorMode.compileForDebug, verbose, exportBinaries, diff --git a/arduino-ide-extension/src/browser/create/create-api.ts b/arduino-ide-extension/src/browser/create/create-api.ts index 77090071a..1e8740a96 100644 --- a/arduino-ide-extension/src/browser/create/create-api.ts +++ b/arduino-ide-extension/src/browser/create/create-api.ts @@ -1,4 +1,4 @@ -import { injectable, inject } from 'inversify'; +import { injectable, inject } from '@theia/core/shared/inversify'; import * as createPaths from './create-paths'; import { posix } from './create-paths'; import { AuthenticationClientService } from '../auth/authentication-client-service'; @@ -117,11 +117,11 @@ export class CreateApi { headers, }) ).sketches; - if (partialSketches.length != 0) { + if (partialSketches.length !== 0) { result.sketches = result.sketches.concat(partialSketches); } currentOffset = currentOffset + limit; - } while (partialSketches.length != 0); + } while (partialSketches.length !== 0); result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch)); return result.sketches; diff --git a/arduino-ide-extension/src/browser/create/create-fs-provider.ts b/arduino-ide-extension/src/browser/create/create-fs-provider.ts index 091ebc979..0d0d1ecb3 100644 --- a/arduino-ide-extension/src/browser/create/create-fs-provider.ts +++ b/arduino-ide-extension/src/browser/create/create-fs-provider.ts @@ -1,4 +1,4 @@ -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { Event } from '@theia/core/lib/common/event'; import { diff --git a/arduino-ide-extension/src/browser/data/dark.color-theme.json b/arduino-ide-extension/src/browser/data/dark.color-theme.json new file mode 100644 index 000000000..072e9a300 --- /dev/null +++ b/arduino-ide-extension/src/browser/data/dark.color-theme.json @@ -0,0 +1,149 @@ +{ + "name": "Arduino dark", + "type": "dark", + "colors": { + "list.highlightForeground": "#0ca1a6", + "list.activeSelectionForeground": "#dae3e3", + "list.activeSelectionBackground": "#434f54", + "list.inactiveSelectionForeground": "#dae3e3", + "list.inactiveSelectionBackground": "#434f54", + "list.hoverBackground": "#1f272a", + "progressBar.background": "#005c5f", + "editor.background": "#1f272a", + "editor.foreground": "#dae3e3", + "editor.lineHighlightBackground": "#434f5410", + "editor.selectionBackground": "#f1c40f", + "editorCursor.foreground": "#434f54", + "editorWhitespace.foreground": "#bfbfbf", + "editorWidget.background": "#171e21", + "focusBorder": "#dae3e3", + "menubar.selectionBackground": "#ffffff", + "menubar.selectionForeground": "#212121", + "menu.selectionBackground": "#dae3e3", + "menu.selectionForeground": "#212121", + "editorGroupHeader.tabsBackground": "#171e21", + "button.background": "#0ca1a6", + "titleBar.activeBackground": "#171e21", + "titleBar.activeForeground": "#dae3e3", + "terminal.background": "#000000", + "terminal.foreground": "#e0e0e0", + "dropdown.border": "#7fcbcd", + "dropdown.background": "#2c353a", + "dropdown.foreground": "#dae3e3", + "activityBar.background": "#171e21", + "activityBar.foreground": "#dae3e3", + "activityBar.inactiveForeground": "#4e5b61", + "activityBar.activeBorder": "#0ca1a6", + "statusBar.background": "#171e21", + "secondaryButton.background": "#ff000000", + "secondaryButton.foreground": "#dae3e3", + "secondaryButton.hoverBackground": "#434f54", + "arduino.branding.primary": "#0ca1a6", + "arduino.branding.secondary": "#b5c8c9", + "arduino.foreground": "#edf1f1", + "arduino.output.foreground": "#ffffff", + "arduino.output.background": "#000000", + "arduino.toolbar.hoverBackground": "#dae3e3", + "sideBar.background": "#101618", + "input.background": "#000000", + "foreground": "#dae3e3", + "settings.headerForeground": "#dae3e3", + "tree.indentGuidesStroke": "#374146", + "tab.unfocusedActiveForeground": "#dae3e3", + "tab.inactiveBackground": "#171e21" + }, + "tokenColors": [ + { + "name": "", + "settings": { + "foreground": "#dae3e3" + } + }, + { + "name": "Comments", + "scope": "comment", + "settings": { + "foreground": "#7f8c8d" + } + }, + { + "name": "Keywords Attributes", + "scope": [ + "storage", + "support", + "string.quoted.single.c" + ], + "settings": { + "foreground": "#0ca1a6" + } + }, + { + "name": "literal", + "scope": [ + "meta.function.c", + "entity.name.function", + "meta.function-call.c", + "variable.other" + ], + "settings": { + "foreground": "#F39C12" + } + }, + { + "name": "punctuation", + "scope": [ + "punctuation.section", + "meta.function-call.c", + "meta.block.c", + "meta.function.c", + "variable", + "variable.name" + ], + "settings": { + "foreground": "#dae3e3" + } + }, + { + "name": "function preprocessor", + "scope": [ + "entity.name.function.preprocessor.c", + "meta.preprocessor.macro.c" + ], + "settings": { + "foreground": "#569CD6" + } + }, + { + "name": "constants", + "scope": [ + "string.quoted.double", + "string.quoted.other.lt-gt", + "constant" + ], + "settings": { + "foreground": "#7fcbcd" + } + }, + { + "name": "meta keywords", + "scope": [ + "keyword.control", + "meta.preprocessor.c" + ], + "settings": { + "foreground": "#C586C0" + } + }, + { + "name": "numeric preprocessor", + "scope": [ + "meta.preprocessor.macro.c", + "constant.numeric.preprocessor.c", + "meta.preprocessor.c" + ], + "settings": { + "foreground": "#434f54" + } + } + ] +} diff --git a/arduino-ide-extension/src/browser/data/default.color-theme.json b/arduino-ide-extension/src/browser/data/default.color-theme.json new file mode 100644 index 000000000..2f4660652 --- /dev/null +++ b/arduino-ide-extension/src/browser/data/default.color-theme.json @@ -0,0 +1,149 @@ +{ + "name": "Arduino default", + "type": "default", + "colors": { + "list.highlightForeground": "#008184", + "list.activeSelectionForeground": "#4e5b61", + "list.activeSelectionBackground": "#dae3e3", + "list.inactiveSelectionForeground": "#4e5b61", + "list.inactiveSelectionBackground": "#dae3e3", + "list.hoverBackground": "#ecf1f1", + "progressBar.background": "#005c5f", + "editor.background": "#ffffff", + "editor.foreground": "#4e5b61", + "editor.lineHighlightBackground": "#434f5410", + "editor.selectionBackground": "#f1c40f", + "editorCursor.foreground": "#434f54", + "editorWhitespace.foreground": "#bfbfbf", + "editorWidget.background": "#f7f9f9", + "focusBorder": "#7fcbcd", + "menubar.selectionBackground": "#ffffff", + "menubar.selectionForeground": "#212121", + "menu.selectionBackground": "#dae3e3", + "menu.selectionForeground": "#212121", + "editorGroupHeader.tabsBackground": "#ecf1f1", + "button.background": "#7fcbcd", + "titleBar.activeBackground": "#006d70", + "titleBar.activeForeground": "#f7f9f9", + "terminal.background": "#000000", + "terminal.foreground": "#e0e0e0", + "dropdown.border": "#f7f9f9", + "dropdown.background": "#ffffff", + "dropdown.foreground": "#4e5b61", + "activityBar.background": "#ecf1f1", + "activityBar.foreground": "#4e5b61", + "activityBar.inactiveForeground": "#bdc7c7", + "activityBar.activeBorder": "#008184", + "statusBar.background": "#006d70", + "secondaryButton.background": "#ff000000", + "secondaryButton.foreground": "#008184", + "secondaryButton.hoverBackground": "#dae3e3", + "arduino.branding.primary": "#008184", + "arduino.branding.secondary": "#b5c8c9", + "arduino.foreground": "#edf1f1", + "arduino.output.foreground": "#ffffff", + "arduino.output.background": "#000000", + "arduino.toolbar.hoverBackground": "#f7f9f9", + "sideBar.background": "#f7f9f9", + "input.background": "#ffffff", + "foreground": "#4e5b61", + "settings.headerForeground": "#4e5b61", + "tree.indentGuidesStroke": "#dae3e3", + "tab.unfocusedActiveForeground": "#4e5b61", + "tab.inactiveBackground": "#ecf1f1" + }, + "tokenColors": [ + { + "name": "", + "settings": { + "foreground": "#434f54" + } + }, + { + "name": "Comments", + "scope": "comment", + "settings": { + "foreground": "#95a5a6cc" + } + }, + { + "name": "Keywords Attributes", + "scope": [ + "storage", + "support", + "string.quoted.single.c" + ], + "settings": { + "foreground": "#00979D" + } + }, + { + "name": "literal", + "scope": [ + "meta.function.c", + "entity.name.function", + "meta.function-call.c", + "variable.other" + ], + "settings": { + "foreground": "#D35400" + } + }, + { + "name": "punctuation", + "scope": [ + "punctuation.section", + "meta.function-call.c", + "meta.block.c", + "meta.function.c", + "variable", + "variable.name" + ], + "settings": { + "foreground": "#434f54" + } + }, + { + "name": "function preprocessor", + "scope": [ + "entity.name.function.preprocessor.c", + "meta.preprocessor.macro.c" + ], + "settings": { + "foreground": "#9e846d" + } + }, + { + "name": "constants", + "scope": [ + "string.quoted.double", + "string.quoted.other.lt-gt", + "constant" + ], + "settings": { + "foreground": "#005C5F" + } + }, + { + "name": "meta keywords", + "scope": [ + "keyword.control", + "meta.preprocessor.c" + ], + "settings": { + "foreground": "#728E00" + } + }, + { + "name": "numeric preprocessor", + "scope": [ + "meta.preprocessor.macro.c", + "constant.numeric.preprocessor.c", + "meta.preprocessor.c" + ], + "settings": { + "foreground": "#434f54" + } + } + ] +} diff --git a/arduino-ide-extension/src/browser/dialogs/certificate-uploader/certificate-add-new.tsx b/arduino-ide-extension/src/browser/dialogs/certificate-uploader/certificate-add-new.tsx index 2cb3e3253..e1084e17a 100644 --- a/arduino-ide-extension/src/browser/dialogs/certificate-uploader/certificate-add-new.tsx +++ b/arduino-ide-extension/src/browser/dialogs/certificate-uploader/certificate-add-new.tsx @@ -1,5 +1,5 @@ import { nls } from '@theia/core/lib/common'; -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; export const CertificateAddComponent = ({ addCertificate, @@ -8,9 +8,12 @@ export const CertificateAddComponent = ({ }): React.ReactElement => { const [value, setValue] = React.useState(''); - const handleChange = React.useCallback((event) => { - setValue(event.target.value); - }, []); + const handleChange = React.useCallback( + (event: React.ChangeEvent) => { + setValue(event.target.value); + }, + [] + ); return (
{ constructor( @inject(UploadCertificateDialogProps) - protected readonly props: UploadCertificateDialogProps + protected override readonly props: UploadCertificateDialogProps ) { super({ title: nls.localize( @@ -155,7 +155,7 @@ export class UploadCertificateDialog extends AbstractDialog { return; } - protected onAfterAttach(msg: Message): void { + protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } @@ -165,21 +165,21 @@ export class UploadCertificateDialog extends AbstractDialog { this.update(); } - protected onUpdateRequest(msg: Message): void { + protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); this.widget.activate(); } - protected handleEnter(event: KeyboardEvent): boolean | void { + protected override handleEnter(event: KeyboardEvent): boolean | void { return false; } - close(): void { + override close(): void { if (this.busy) { return; } diff --git a/arduino-ide-extension/src/browser/dialogs/certificate-uploader/select-board-components.tsx b/arduino-ide-extension/src/browser/dialogs/certificate-uploader/select-board-components.tsx index f852e04fb..285f136c3 100644 --- a/arduino-ide-extension/src/browser/dialogs/certificate-uploader/select-board-components.tsx +++ b/arduino-ide-extension/src/browser/dialogs/certificate-uploader/select-board-components.tsx @@ -1,5 +1,5 @@ import { nls } from '@theia/core/lib/common'; -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; import { AvailableBoard } from '../../boards/boards-service-provider'; import { ArduinoSelect } from '../../widgets/arduino-select'; diff --git a/arduino-ide-extension/src/browser/dialogs/cloud-share-sketch-dialog.tsx b/arduino-ide-extension/src/browser/dialogs/cloud-share-sketch-dialog.tsx index 3ecf1e28d..dbbad77e6 100644 --- a/arduino-ide-extension/src/browser/dialogs/cloud-share-sketch-dialog.tsx +++ b/arduino-ide-extension/src/browser/dialogs/cloud-share-sketch-dialog.tsx @@ -1,7 +1,7 @@ -import * as React from 'react'; -import { inject, injectable } from 'inversify'; -import { Widget } from '@phosphor/widgets'; -import { Message } from '@phosphor/messaging'; +import * as React from '@theia/core/shared/react'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { Widget } from '@theia/core/shared/@phosphor/widgets'; +import { Message } from '@theia/core/shared/@phosphor/messaging'; import { clipboard } from 'electron'; import { ReactWidget, DialogProps } from '@theia/core/lib/browser'; import { AbstractDialog } from '../theia/dialogs/dialogs'; @@ -149,7 +149,7 @@ export class ShareSketchDialog extends AbstractDialog { constructor( @inject(ShareSketchDialogProps) - protected readonly props: ShareSketchDialogProps + protected override readonly props: ShareSketchDialogProps ) { super({ title: props.title }); this.contentNode.classList.add('arduino-share-sketch-dialog'); @@ -159,7 +159,7 @@ export class ShareSketchDialog extends AbstractDialog { get value(): void { return; } - protected onAfterAttach(msg: Message): void { + protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } @@ -168,12 +168,12 @@ export class ShareSketchDialog extends AbstractDialog { this.update(); } - protected onUpdateRequest(msg: Message): void { + protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); this.widget.activate(); } diff --git a/arduino-ide-extension/src/browser/dialogs/do-not-ask-again-dialog.ts b/arduino-ide-extension/src/browser/dialogs/do-not-ask-again-dialog.ts index eb23fec0a..a7982aca5 100644 --- a/arduino-ide-extension/src/browser/dialogs/do-not-ask-again-dialog.ts +++ b/arduino-ide-extension/src/browser/dialogs/do-not-ask-again-dialog.ts @@ -1,5 +1,5 @@ -import { inject, injectable } from 'inversify'; -import { Widget } from '@phosphor/widgets'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { CancellationTokenSource } from '@theia/core/lib/common/cancellation'; import { ConfirmDialog, @@ -19,7 +19,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog { constructor( @inject(DoNotAskAgainDialogProps) - protected readonly props: DoNotAskAgainDialogProps + protected override readonly props: DoNotAskAgainDialogProps ) { super(props); this.controlPanel.removeChild(this.errorMessageNode); @@ -42,7 +42,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog { this.doNotAskAgainCheckbox.type = 'checkbox'; } - protected async accept(): Promise { + protected override async accept(): Promise { if (!this.resolve) { return; } @@ -65,7 +65,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog { } } - protected setErrorMessage(error: DialogError): void { + protected override setErrorMessage(error: DialogError): void { if (this.acceptButton) { this.acceptButton.disabled = !DialogError.getResult(error); } diff --git a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx index 8e5c6d32d..8dd12e681 100644 --- a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx @@ -1,5 +1,6 @@ import { nls } from '@theia/core/lib/common'; -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; +import { Port } from '../../../common/protocol'; import { ArduinoFirmwareUploader, FirmwareInfo, @@ -20,7 +21,7 @@ export const FirmwareUploaderComponent = ({ availableBoards: AvailableBoard[]; firmwareUploader: ArduinoFirmwareUploader; updatableFqbns: string[]; - flashFirmware: (firmware: FirmwareInfo, port: string) => Promise; + flashFirmware: (firmware: FirmwareInfo, port: Port) => Promise; isOpen: any; }): React.ReactElement => { // boolean states for buttons @@ -81,7 +82,7 @@ export const FirmwareUploaderComponent = ({ const installStatus = !!firmwareToFlash && !!selectedBoard?.port && - (await flashFirmware(firmwareToFlash, selectedBoard?.port.address)); + (await flashFirmware(firmwareToFlash, selectedBoard?.port)); setInstallFeedback((installStatus && 'ok') || 'fail'); } catch { diff --git a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx index 759757e39..ff65b71a9 100644 --- a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx +++ b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx @@ -1,9 +1,13 @@ -import * as React from 'react'; -import { inject, injectable, postConstruct } from 'inversify'; +import * as React from '@theia/core/shared/react'; +import { + inject, + injectable, + postConstruct, +} from '@theia/core/shared/inversify'; import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { AbstractDialog } from '../../theia/dialogs/dialogs'; -import { Widget } from '@phosphor/widgets'; -import { Message } from '@phosphor/messaging'; +import { Widget } from '@theia/core/shared/@phosphor/widgets'; +import { Message } from '@theia/core/shared/@phosphor/messaging'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { AvailableBoard, @@ -15,6 +19,8 @@ import { } from '../../../common/protocol/arduino-firmware-uploader'; import { FirmwareUploaderComponent } from './firmware-uploader-component'; import { UploadFirmware } from '../../contributions/upload-firmware'; +import { Port } from '../../../common/protocol'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; @injectable() export class UploadFirmwareDialogWidget extends ReactWidget { @@ -24,6 +30,9 @@ export class UploadFirmwareDialogWidget extends ReactWidget { @inject(ArduinoFirmwareUploader) protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader; + @inject(FrontendApplicationStateService) + private readonly appStatusService: FrontendApplicationStateService; + protected updatableFqbns: string[] = []; protected availableBoards: AvailableBoard[] = []; protected isOpen = new Object(); @@ -38,7 +47,8 @@ export class UploadFirmwareDialogWidget extends ReactWidget { @postConstruct() protected init(): void { - this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => { + this.appStatusService.reachedState('ready').then(async () => { + const fqbns = await this.arduinoFirmwareUploader.updatableBoards(); this.updatableFqbns = fqbns; this.update(); }); @@ -49,14 +59,14 @@ export class UploadFirmwareDialogWidget extends ReactWidget { }); } - protected flashFirmware(firmware: FirmwareInfo, port: string): Promise { + protected flashFirmware(firmware: FirmwareInfo, port: Port): Promise { this.busyCallback(true); return this.arduinoFirmwareUploader .flash(firmware, port) .finally(() => this.busyCallback(false)); } - onCloseRequest(msg: Message): void { + protected override onCloseRequest(msg: Message): void { super.onCloseRequest(msg); this.isOpen = new Object(); } @@ -88,7 +98,7 @@ export class UploadFirmwareDialog extends AbstractDialog { constructor( @inject(UploadFirmwareDialogProps) - protected readonly props: UploadFirmwareDialogProps + protected override readonly props: UploadFirmwareDialogProps ) { super({ title: UploadFirmware.Commands.OPEN.label || '' }); this.contentNode.classList.add('firmware-uploader-dialog'); @@ -99,7 +109,7 @@ export class UploadFirmwareDialog extends AbstractDialog { return; } - protected onAfterAttach(msg: Message): void { + protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } @@ -109,21 +119,21 @@ export class UploadFirmwareDialog extends AbstractDialog { this.update(); } - protected onUpdateRequest(msg: Message): void { + protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); this.widget.activate(); } - protected handleEnter(event: KeyboardEvent): boolean | void { + protected override handleEnter(event: KeyboardEvent): boolean | void { return false; } - close(): void { + override close(): void { if (this.busy) { return; } diff --git a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx index d8b94bd94..262aea518 100644 --- a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx @@ -1,8 +1,8 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { nls } from '@theia/core/lib/common'; import { shell } from 'electron'; -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as React from '@theia/core/shared/react'; +import * as ReactDOM from '@theia/core/shared/react-dom'; import ReactMarkdown from 'react-markdown'; import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater'; import ProgressBar from '../../components/ProgressBar'; diff --git a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx index 97642b39d..2b0b952bd 100644 --- a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx +++ b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx @@ -1,9 +1,9 @@ -import * as React from 'react'; -import { inject, injectable } from 'inversify'; +import * as React from '@theia/core/shared/react'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { AbstractDialog } from '../../theia/dialogs/dialogs'; -import { Widget } from '@phosphor/widgets'; -import { Message } from '@phosphor/messaging'; +import { Widget } from '@theia/core/shared/@phosphor/widgets'; +import { Message } from '@theia/core/shared/@phosphor/messaging'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { nls } from '@theia/core'; import { IDEUpdaterComponent } from './ide-updater-component'; @@ -70,7 +70,7 @@ export class IDEUpdaterDialogWidget extends ReactWidget { this.close(); } - close(): void { + override close(): void { super.close(); this.onClose(); } @@ -122,7 +122,7 @@ export class IDEUpdaterDialog extends AbstractDialog { constructor( @inject(IDEUpdaterDialogProps) - protected readonly props: IDEUpdaterDialogProps + protected override readonly props: IDEUpdaterDialogProps ) { super({ title: nls.localize( @@ -138,7 +138,7 @@ export class IDEUpdaterDialog extends AbstractDialog { return this.widget.updateInfo; } - protected onAfterAttach(msg: Message): void { + protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } @@ -147,7 +147,7 @@ export class IDEUpdaterDialog extends AbstractDialog { this.update(); } - async open( + override async open( data: UpdateInfo | undefined = undefined ): Promise { if (data && data.version) { @@ -156,17 +156,17 @@ export class IDEUpdaterDialog extends AbstractDialog { } } - protected onUpdateRequest(msg: Message): void { + protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } - protected onActivateRequest(msg: Message): void { + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); this.widget.activate(); } - close(): void { + override close(): void { this.widget.dispose(); super.close(); } diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx index 9c1563547..86d0d6847 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import * as React from '@theia/core/shared/react'; import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import 'react-tabs/style/react-tabs.css'; import { Disable } from 'react-disable'; @@ -17,7 +17,10 @@ import { import { nls } from '@theia/core/lib/common'; import { Settings, SettingsService } from './settings'; import { AdditionalUrlsDialog } from './settings-dialog'; -import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization'; +import { + AsyncLocalizationProvider, + LanguageInfo, +} from '@theia/core/lib/common/i18n/localization'; export class SettingsComponent extends React.Component< SettingsComponent.Props, @@ -29,7 +32,7 @@ export class SettingsComponent extends React.Component< super(props); } - componentDidUpdate( + override componentDidUpdate( _: SettingsComponent.Props, prevState: SettingsComponent.State ): void { @@ -46,7 +49,7 @@ export class SettingsComponent extends React.Component< } } - componentDidMount(): void { + override componentDidMount(): void { this.props.settingsService .settings() .then((settings) => @@ -64,11 +67,11 @@ export class SettingsComponent extends React.Component< ]); } - componentWillUnmount(): void { + override componentWillUnmount(): void { this.toDispose.dispose(); } - render(): React.ReactNode { + override render(): React.ReactNode { if (!this.state) { return
; } @@ -213,11 +216,9 @@ export class SettingsComponent extends React.Component< value={this.state.currentLanguage} onChange={this.languageDidChange} > - {this.state.languages.map((label) => ( - - ))} + {this.state.languages.map((label) => + this.toSelectOptions(label) + )} ( @@ -275,7 +276,7 @@ export class SettingsComponent extends React.Component<