diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1dde3ac42..5d7a186c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Arduino Pro IDE +name: Arduino IDE on: push: @@ -22,7 +22,6 @@ jobs: - os: windows-latest - os: ubuntu-latest - os: macos-latest - # - os: rsora-rpi-arm # self-hosted armhf runs-on: ${{ matrix.config.os }} timeout-minutes: 90 @@ -98,7 +97,7 @@ jobs: if [ "$IS_RELEASE" = true ]; then export BODY=$(echo -e "$GIT_LOG") else - export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-pro-ide/releases/tag/$LATEST_TAG)") + export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-ide/releases/tag/$LATEST_TAG)") if [ -z "$GIT_LOG" ]; then export BODY="There were no changes since version $LATEST_TAG_WITH_LINK." else @@ -135,7 +134,7 @@ jobs: env: PLUGIN_SOURCE: "build-artifacts/*" PLUGIN_STRIP_PREFIX: "build-artifacts/" - PLUGIN_TARGET: "/arduino-pro-ide/nightly" + PLUGIN_TARGET: "/arduino-ide/nightly" PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} @@ -159,8 +158,7 @@ jobs: - name: Publish Release [GitHub] uses: svenstaro/upload-release-action@2.2.0 with: - repo_token: ${{ secrets.RELEASE_TOKEN }} - repo_name: arduino/arduino-pro-ide + repo_token: ${{ secrets.GITHUB_TOKEN }} release_name: ${{ steps.tag_name.outputs.TAG_NAME }} file: build-artifacts/* tag: ${{ github.ref }} @@ -172,7 +170,7 @@ jobs: env: PLUGIN_SOURCE: "build-artifacts/*" PLUGIN_STRIP_PREFIX: "build-artifacts/" - PLUGIN_TARGET: "/arduino-pro-ide" + PLUGIN_TARGET: "/arduino-ide" PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9c3650271..d9a87c6fd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Arduino Pro IDE - Rebuild Electron App", + "label": "Arduino IDE - Rebuild Electron App", "type": "shell", "command": "yarn rebuild:browser && yarn rebuild:electron", "group": "build", @@ -15,7 +15,7 @@ } }, { - "label": "Arduino Pro IDE - Start Browser App", + "label": "Arduino IDE - Start Browser App", "type": "shell", "command": "yarn --cwd ./browser-app start", "group": "build", @@ -26,7 +26,7 @@ } }, { - "label": "Arduino Pro IDE - Watch IDE Extension", + "label": "Arduino IDE - Watch IDE Extension", "type": "shell", "command": "yarn --cwd ./arduino-ide-extension watch", "group": "build", @@ -37,7 +37,7 @@ } } { - "label": "Arduino Pro IDE - Watch Browser App", + "label": "Arduino IDE - Watch Browser App", "type": "shell", "command": "yarn --cwd ./browser-app watch", "group": "build", @@ -48,7 +48,7 @@ } }, { - "label": "Arduino Pro IDE - Watch Electron App", + "label": "Arduino IDE - Watch Electron App", "type": "shell", "command": "yarn --cwd ./electron-app watch", "group": "build", @@ -59,19 +59,19 @@ } }, { - "label": "Arduino Pro IDE - Watch All [Browser]", + "label": "Arduino IDE - Watch All [Browser]", "type": "shell", "dependsOn": [ - "Arduino Pro IDE - Watch IDE Extension", - "Arduino Pro IDE - Watch Browser App" + "Arduino IDE - Watch IDE Extension", + "Arduino IDE - Watch Browser App" ] }, { - "label": "Arduino Pro IDE - Watch All [Electron]", + "label": "Arduino IDE - Watch All [Electron]", "type": "shell", "dependsOn": [ - "Arduino Pro IDE - Watch IDE Extension", - "Arduino Pro IDE - Watch Electron App" + "Arduino IDE - Watch IDE Extension", + "Arduino IDE - Watch Electron App" ] } ] diff --git a/README.md b/README.md index 9ef975b30..34ab360b2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Arduino Pro IDE +# Arduino IDE -[![Arduino Pro IDE](https://github.com/bcmi-labs/arduino-editor/workflows/Arduino%20Pro%20IDE/badge.svg)](https://github.com/bcmi-labs/arduino-editor/actions?query=workflow%3A%22Arduino+Pro+IDE%22) +[![Arduino IDE](https://github.com/bcmi-labs/arduino-editor/workflows/Arduino%20Pro%20IDE/badge.svg)](https://github.com/bcmi-labs/arduino-editor/actions?query=workflow%3A%22Arduino+Pro+IDE%22) ### Download -You can download the latest version of the Arduino Pro IDE application for the supported platforms from the [GitHub release page](https://github.com/arduino/arduino-pro-ide/releases) or following the links in the following table. +You can download the latest version of the Arduino IDE application for the supported platforms from the [GitHub release page](https://github.com/arduino/arduino-ide/releases) or following the links in the following table. #### Latest version @@ -15,16 +15,16 @@ Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] Windows | | [Windows 64 bit installer]
[Windows 64 bit MSI]
[Windows 64 bit ZIP] | macOS | | [macOS 64 bit] | -[🚧 Work in progress...]: https://github.com/arduino/arduino-pro-ide/issues/287 -[Linux 64 bit]: https://downloads.arduino.cc/arduino-pro-ide/arduino-pro-ide_latest_Linux_64bit.zip -[Windows 64 bit installer]: https://downloads.arduino.cc/arduino-pro-ide/arduino-pro-ide_latest_Windows_64bit.exe -[Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-pro-ide/arduino-pro-ide_latest_Windows_64bit.msi -[Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-pro-ide/arduino-pro-ide_latest_Windows_64bit.zip -[macOS 64 bit]: https://downloads.arduino.cc/arduino-pro-ide/arduino-pro-ide_latest_macOS_64bit.dmg +[🚧 Work in progress...]: https://github.com/arduino/arduino-ide/issues/287 +[Linux 64 bit]: https://downloads.arduino.cc/arduino-ide/arduino-ide_latest_Linux_64bit.zip +[Windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/arduino-ide_latest_Windows_64bit.exe +[Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-ide/arduino-ide_latest_Windows_64bit.msi +[Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-ide/arduino-ide_latest_Windows_64bit.zip +[macOS 64 bit]: https://downloads.arduino.cc/arduino-ide/arduino-ide_latest_macOS_64bit.dmg #### Previous versions -These are available from the [GitHub releases page](https://github.com/arduino/arduino-pro-ide/releases). +These are available from the [GitHub releases page](https://github.com/arduino/arduino-ide/releases). #### Nightly builds @@ -39,12 +39,12 @@ Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] Windows | | [Nightly Windows 64 bit installer]
[Nightly Windows 64 bit MSI]
[Nightly Windows 64 bit ZIP] | macOS | | [Nightly macOS 64 bit] | -[🚧 Work in progress...]: https://github.com/arduino/arduino-pro-ide/issues/287 -[Nightly Linux 64 bit]: https://downloads.arduino.cc/arduino-pro-ide/nightly/arduino-pro-ide_nightly-latest_Linux_64bit.zip -[Nightly Windows 64 bit installer]: https://downloads.arduino.cc/arduino-pro-ide/nightly/arduino-pro-ide_nightly-latest_Windows_64bit.exe -[Nightly Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-pro-ide/nightly/arduino-pro-ide_nightly-latest_Windows_64bit.msi -[Nightly Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-pro-ide/nightly/arduino-pro-ide_nightly-latest_Windows_64bit.zip -[Nightly macOS 64 bit]: https://downloads.arduino.cc/arduino-pro-ide/nightly/arduino-pro-ide_nightly-latest_macOS_64bit.dmg +[🚧 Work in progress...]: https://github.com/arduino/arduino-ide/issues/287 +[Nightly Linux 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.zip +[Nightly Windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.exe +[Nightly Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.msi +[Nightly Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.zip +[Nightly macOS 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_macOS_64bit.dmg > These links return an HTTP `302: Found` response, redirecting to latest generated builds by replacing `latest` with the latest available build @@ -54,7 +54,7 @@ macOS | | [Nightly macOS 64 bit] ### Build from source If you’re familiar with TypeScript, the [Theia IDE](https://theia-ide.org/), and if you want to contribute to the -project, you should be able to build the Arduino Pro IDE locally. Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions. +project, you should be able to build the Arduino IDE locally. Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions. ### Build ```sh @@ -114,7 +114,7 @@ git add . \ git tag -a 0.2.0 -m "0.2.0" \ && git push origin 0.2.0 ``` - - The release build starts automatically and uploads the artifacts with the changelog to the Pro IDE [release page](https://github.com/arduino/arduino-pro-ide/releases). + - The release build starts automatically and uploads the artifacts with the changelog to the Pro IDE [release page](https://github.com/arduino/arduino-ide/releases). - If you do not want to release the `EXE` and `MSI` installers, wipe them manually. - If you do not like the generated changelog, modify it and update the GH release. @@ -126,9 +126,9 @@ git tag -a 0.2.0 -m "0.2.0" \ - Q: I have understood that not all versions of the CLI is compatible with my version of IDE but how can I manually update the `arduino-cli` inside the IDE? - A: [Get](https://arduino.github.io/arduino-cli/installation) the desired version of `arduino-cli` for your platform and manually replace the one inside the IDE. The CLI can be found inside the IDE at: - - Windows: `C:\path\to\Arduino Pro IDE\resources\app\node_modules\arduino-ide-extension\build\arduino-cli.exe`, - - macOS: `/path/to/Arduino Pro IDE.app/Contents/Resources/app/node_modules/arduino-ide-extension/build/arduino-cli`, and - - Linux: `/path/to/Arduino Pro IDE/resources/app/node_modules/arduino-ide-extension/build/arduino-cli`. + - Windows: `C:\path\to\Arduino IDE\resources\app\node_modules\arduino-ide-extension\build\arduino-cli.exe`, + - macOS: `/path/to/Arduino IDE.app/Contents/Resources/app/node_modules/arduino-ide-extension/build/arduino-cli`, and + - Linux: `/path/to/Arduino IDE/resources/app/node_modules/arduino-ide-extension/build/arduino-cli`. ### Architecture overview diff --git a/arduino-debugger-extension/package.json b/arduino-debugger-extension/package.json deleted file mode 100644 index a04d390af..000000000 --- a/arduino-debugger-extension/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "arduino-debugger-extension", - "version": "0.1.3", - "description": "An extension for debugging Arduino programs", - "license": "MIT", - "dependencies": { - "@theia/debug": "next", - "arduino-ide-extension": "0.1.3", - "cdt-gdb-adapter": "^0.0.14", - "vscode-debugadapter": "^1.26.0", - "vscode-debugprotocol": "^1.26.0" - }, - "scripts": { - "prepare": "yarn run clean && yarn run build", - "clean": "rimraf lib", - "lint": "tslint -c ./tslint.json --project ./tsconfig.json", - "build": "tsc && yarn lint", - "watch": "tsc -w" - }, - "files": [ - "lib", - "src" - ], - "theiaExtensions": [ - { - "backend": "lib/node/arduino-debug-backend-module", - "frontend": "lib/browser/arduino-debug-frontend-module" - } - ] -} diff --git a/arduino-debugger-extension/src/browser/arduino-debug-configuration-manager.ts b/arduino-debugger-extension/src/browser/arduino-debug-configuration-manager.ts deleted file mode 100644 index 5e15f1cd2..000000000 --- a/arduino-debugger-extension/src/browser/arduino-debug-configuration-manager.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { DebugConfigurationManager } from "@theia/debug/lib/browser/debug-configuration-manager"; -import { injectable } from "inversify"; - -@injectable() -export class ArduinoDebugConfigurationManager extends DebugConfigurationManager { - - get defaultDebugger(): Promise { - return this.debug.getDebuggersForLanguage('ino').then(debuggers => { - if (debuggers.length === 0) - return undefined; - return debuggers[0].type; - }); - } - - protected async selectDebugType(): Promise { - const widget = this.editorManager.currentEditor; - if (!widget) { - return this.defaultDebugger; - } - const { languageId } = widget.editor.document; - const debuggers = await this.debug.getDebuggersForLanguage(languageId); - if (debuggers.length === 0) { - return this.defaultDebugger; - } - return this.quickPick.show(debuggers.map( - ({ label, type }) => ({ label, value: type }), - { placeholder: 'Select Environment' }) - ); - } - - async createDefaultConfiguration(): Promise { - const { model } = this; - if (model) { - await this.doCreate(model); - await this.updateModels(); - } - } - -} diff --git a/arduino-debugger-extension/src/browser/arduino-debug-frontend-application-contribution.ts b/arduino-debugger-extension/src/browser/arduino-debug-frontend-application-contribution.ts deleted file mode 100644 index c37115a0a..000000000 --- a/arduino-debugger-extension/src/browser/arduino-debug-frontend-application-contribution.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { injectable, inject } from 'inversify'; -import { MenuModelRegistry, MessageService, Command, CommandRegistry } from '@theia/core'; -import { KeybindingRegistry } from '@theia/core/lib/browser'; -import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { DebugFrontendApplicationContribution, DebugCommands } from '@theia/debug/lib/browser/debug-frontend-application-contribution'; -import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options"; -import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; -import { FileSystem } from '@theia/filesystem/lib/common'; -import URI from '@theia/core/lib/common/uri'; -import { EditorManager } from '@theia/editor/lib/browser'; -import { EditorMode } from "arduino-ide-extension/lib/browser/editor-mode"; -import { SketchesService } from 'arduino-ide-extension/lib/common/protocol/sketches-service'; -import { ArduinoToolbar } from 'arduino-ide-extension/lib/browser/toolbar/arduino-toolbar'; -import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager'; - -export namespace ArduinoDebugCommands { - export const START_DEBUG: Command = { - id: 'arduino-start-debug', - label: 'Start Debugging' - } -} - -@injectable() -export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendApplicationContribution { - - @inject(EditorMode) - protected readonly editorMode: EditorMode; - - @inject(WorkspaceService) - protected readonly workspaceService: WorkspaceService; - - @inject(SketchesService) - protected readonly sketchesService: SketchesService; - - @inject(FileSystem) - protected readonly fileSystem: FileSystem; - - @inject(EditorManager) - protected readonly editorManager: EditorManager; - - @inject(MessageService) - protected readonly messageService: MessageService; - - async start(noDebug?: boolean, debugSessionOptions?: DebugSessionOptions): Promise { - const configurations = this.configurations as ArduinoDebugConfigurationManager; - let current = debugSessionOptions ? debugSessionOptions : configurations.current; - // If no configurations are currently present, create them - if (!current) { - await configurations.createDefaultConfiguration(); - current = configurations.current; - } - if (current) { - if (noDebug !== undefined) { - current = { - ...current, - configuration: { - ...current.configuration, - noDebug - } - }; - } - if (current.configuration.type === 'arduino') { - const wsStat = this.workspaceService.workspace; - let sketchFileURI: URI | undefined; - if (wsStat && await this.sketchesService.isSketchFolder(wsStat.resource.toString())) { - const wsPath = wsStat.resource.path; - const sketchFilePath = wsPath.join(wsPath.name + '.ino').toString(); - sketchFileURI = new URI(sketchFilePath); - } else if (this.editorManager.currentEditor) { - const editorURI = this.editorManager.currentEditor.getResourceUri(); - if (editorURI && editorURI.path && editorURI.path.ext === '.ino') { - sketchFileURI = editorURI; - } - } - if (sketchFileURI) { - await this.editorManager.open(sketchFileURI); - await this.manager.start(current); - } else { - this.messageService.error('Please open a sketch file to start debugging.') - } - } else { - await this.manager.start(current); - } - } - } - - initializeLayout(): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(); - } - return Promise.resolve(); - } - - registerMenus(menus: MenuModelRegistry): void { - if (this.editorMode.proMode) { - super.registerMenus(menus); - menus.unregisterMenuAction(DebugCommands.START_NO_DEBUG); - } - } - - registerKeybindings(keybindings: KeybindingRegistry): void { - if (this.editorMode.proMode) { - super.registerKeybindings(keybindings); - keybindings.unregisterKeybinding({ - command: DebugCommands.START_NO_DEBUG.id, - keybinding: 'ctrl+f5' - }); - } - } - - registerToolbarItems(toolbar: TabBarToolbarRegistry): void { - super.registerToolbarItems(toolbar); - toolbar.registerItem({ - id: ArduinoDebugCommands.START_DEBUG.id, - command: ArduinoDebugCommands.START_DEBUG.id, - tooltip: 'Start Debugging', - priority: 3 - }); - } - - registerCommands(registry: CommandRegistry): void { - super.registerCommands(registry); - registry.registerCommand(ArduinoDebugCommands.START_DEBUG, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - isEnabled: widget => ArduinoToolbar.is(widget) && widget.side === 'left', - execute: () => { - registry.executeCommand(DebugCommands.START.id); - } - }); - } - - -} diff --git a/arduino-debugger-extension/src/browser/arduino-debug-frontend-module.ts b/arduino-debugger-extension/src/browser/arduino-debug-frontend-module.ts deleted file mode 100644 index 792b2a12a..000000000 --- a/arduino-debugger-extension/src/browser/arduino-debug-frontend-module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ContainerModule } from 'inversify'; -import { VariableContribution } from '@theia/variable-resolver/lib/browser'; -import { ArduinoVariableResolver } from './arduino-variable-resolver'; -import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager'; -import { DebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution'; -import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager'; -import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager'; -import { ArduinoDebugFrontendApplicationContribution } from './arduino-debug-frontend-application-contribution'; -import { ArduinoDebugSessionManager } from './arduino-debug-session-manager'; - -import '../../src/browser/style/index.css'; - -export default new ContainerModule((bind, unbind, isBound, rebind) => { - bind(ArduinoVariableResolver).toSelf().inSingletonScope(); - bind(VariableContribution).toService(ArduinoVariableResolver); - rebind(DebugSessionManager).to(ArduinoDebugSessionManager).inSingletonScope(); - rebind(DebugConfigurationManager).to(ArduinoDebugConfigurationManager).inSingletonScope(); - rebind(DebugFrontendApplicationContribution).to(ArduinoDebugFrontendApplicationContribution); -}); diff --git a/arduino-debugger-extension/src/browser/arduino-debug-session-manager.ts b/arduino-debugger-extension/src/browser/arduino-debug-session-manager.ts deleted file mode 100644 index 79f81d58c..000000000 --- a/arduino-debugger-extension/src/browser/arduino-debug-session-manager.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DebugSessionManager } from "@theia/debug/lib/browser/debug-session-manager"; -import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options"; - -export class ArduinoDebugSessionManager extends DebugSessionManager { - - start(options: DebugSessionOptions) { - if (options.configuration.type === 'arduino' && this.sessions.find(s => s.configuration.type === 'arduino')) { - this.messageService.info('A debug session is already running. You must stop the running session before starting a new one.') - return Promise.resolve(undefined); - } - return super.start(options); - } - -} diff --git a/arduino-debugger-extension/src/browser/arduino-variable-resolver.ts b/arduino-debugger-extension/src/browser/arduino-variable-resolver.ts deleted file mode 100644 index faee5c9e6..000000000 --- a/arduino-debugger-extension/src/browser/arduino-variable-resolver.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser'; -import { injectable, inject } from 'inversify'; -import { MessageService } from '@theia/core/lib/common/message-service'; -import { BoardsServiceProvider } from 'arduino-ide-extension/lib/browser/boards/boards-service-provider'; - -@injectable() -export class ArduinoVariableResolver implements VariableContribution { - - @inject(BoardsServiceProvider) - protected readonly boardsServiceProvider: BoardsServiceProvider; - - @inject(MessageService) - protected readonly messageService: MessageService - - registerVariables(variables: VariableRegistry): void { - variables.registerVariable({ - name: 'fqbn', - description: 'Qualified name of the selected board', - resolve: this.resolveFqbn.bind(this), - }); - variables.registerVariable({ - name: 'port', - description: 'Selected upload port', - resolve: this.resolvePort.bind(this) - }); - } - - protected async resolveFqbn(): Promise { - const { boardsConfig } = this.boardsServiceProvider; - if (!boardsConfig || !boardsConfig.selectedBoard) { - this.messageService.error('No board selected. Please select a board for debugging.'); - return undefined; - } - return boardsConfig.selectedBoard.fqbn; - } - - protected async resolvePort(): Promise { - const { boardsConfig } = this.boardsServiceProvider; - if (!boardsConfig || !boardsConfig.selectedPort) { - return undefined; - } - return boardsConfig.selectedPort.address; - } - -} diff --git a/arduino-debugger-extension/src/browser/style/index.css b/arduino-debugger-extension/src/browser/style/index.css deleted file mode 100644 index 1e3d51a60..000000000 --- a/arduino-debugger-extension/src/browser/style/index.css +++ /dev/null @@ -1,16 +0,0 @@ -.arduino-start-debug-icon { - -webkit-mask: url('debug-dark.svg') 50%; - mask: url('debug-dark.svg') 50%; - -webkit-mask-size: 100%; - mask-size: 100%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - display: flex; - justify-content: center; - align-items: center; - color: var(--theia-ui-button-font-color); -} - -.arduino-start-debug { - border-radius: 12px; -} diff --git a/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts b/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts deleted file mode 100644 index 164b4be4d..000000000 --- a/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as path from 'path'; -import { injectable, inject } from 'inversify'; -import { DebugAdapterContribution, DebugAdapterExecutable } from '@theia/debug/lib/common/debug-model'; -import { DebugConfiguration } from '@theia/debug/lib/common/debug-configuration'; -import { IJSONSchema } from '@theia/core/lib/common/json-schema'; -import { ArduinoDaemonImpl } from 'arduino-ide-extension/lib/node/arduino-daemon-impl'; - -@injectable() -export class ArduinoDebugAdapterContribution implements DebugAdapterContribution { - - readonly type = 'arduino'; - readonly label = 'Arduino'; - readonly languages = ['c', 'cpp', 'ino']; - - @inject(ArduinoDaemonImpl) daemon: ArduinoDaemonImpl; - - getSchemaAttributes(): IJSONSchema[] { - return [ - { - 'properties': { - 'sketch': { - 'type': 'string', - 'description': 'path to the sketch root ino file', - 'default': '${file}', - }, - 'pauseAtMain': { - 'description': 'If enabled the debugger will pause at the start of the main function.', - 'type': 'boolean', - 'default': false - }, - 'debugDebugAdapter': { - 'type': 'boolean', - 'description': 'Start the debug adapter in debug mode (with --inspect-brk)', - 'default': false - }, - } - } - ] - } - - provideDebugAdapterExecutable(config: DebugConfiguration): DebugAdapterExecutable { - const debugAdapterMain = path.join(__dirname, 'debug-adapter', 'main'); - if (config.debugDebugAdapter) { - return { - command: process.execPath, - args: ['--inspect-brk', debugAdapterMain], - } - } - return { - modulePath: debugAdapterMain, - args: [], - } - } - - provideDebugConfigurations(): DebugConfiguration[] { - return [ - { - name: this.label, - type: this.type, - request: 'launch' - } - ]; - } - - async resolveDebugConfiguration(config: DebugConfiguration): Promise { - const startFunction = config.pauseAtMain ? 'main' : 'setup'; - const res: ActualDebugConfig = { - ...config, - arduinoCli: await this.daemon.getExecPath(), - fqbn: '${fqbn}', - uploadPort: '${port}', - initCommands: [ - `-break-insert -t --function ${startFunction}` - ] - } - if (!res.sketch) { - res.sketch = '${file}'; - } - return res; - } - -} - -interface ActualDebugConfig extends DebugConfiguration { - arduinoCli?: string; - sketch?: string; - fqbn?: string; - uploadPort?: string; -} diff --git a/arduino-debugger-extension/src/node/arduino-debug-backend-module.ts b/arduino-debugger-extension/src/node/arduino-debug-backend-module.ts deleted file mode 100644 index f9e729724..000000000 --- a/arduino-debugger-extension/src/node/arduino-debug-backend-module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ContainerModule } from 'inversify'; -import { DebugAdapterContribution } from '@theia/debug/lib/common/debug-model'; -import { ArduinoDebugAdapterContribution } from './arduino-debug-adapter-contribution'; - -export default new ContainerModule((bind, unbind, isBound, rebind) => { - bind(DebugAdapterContribution).to(ArduinoDebugAdapterContribution).inSingletonScope(); -}); diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts deleted file mode 100644 index 87eba74ac..000000000 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { DebugProtocol } from 'vscode-debugprotocol'; -import { GDBDebugSession, FrameVariableReference } from 'cdt-gdb-adapter/dist/GDBDebugSession'; -import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend'; -import * as mi from 'cdt-gdb-adapter/dist/mi'; -import { ArduinoGDBBackend } from './arduino-gdb-backend'; -import { ArduinoVariableHandler } from './arduino-variable-handler'; -import { Scope, OutputEvent } from 'vscode-debugadapter'; - -export interface ArduinoLaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { - arduinoCli?: string; - sketch?: string; - fqbn?: string; - uploadPort?: string; -} - -const GLOBAL_HANDLE_ID = 0xFE; -const STATIC_HANDLES_START = 0x010000; -const STATIC_HANDLES_FINISH = 0x01FFFF; - -export class ArduinoDebugSession extends GDBDebugSession { - - private _variableHandler: ArduinoVariableHandler; - - get arduinoBackend(): ArduinoGDBBackend { - return this.gdb as ArduinoGDBBackend; - } - - protected get variableHandler() { - if (this._variableHandler) { - return this._variableHandler; - } - if (!this.gdb) { - throw new Error("GDB backend is not ready."); - } - const handler = new ArduinoVariableHandler(this, this.frameHandles, this.variableHandles); - this._variableHandler = handler; - return handler; - } - - protected createBackend(): GDBBackend { - return new ArduinoGDBBackend(); - } - - protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise { - try { - await this.gdb.sendCommand('-interpreter-exec console "monitor reset halt"') - await mi.sendExecContinue(this.gdb); - this.sendResponse(response); - } catch (err) { - this.sendErrorResponse(response, 100, err.message); - } - } - - protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments): Promise { - if (process.platform === 'win32') { - const message = 'Pause is not supported on Windows. Please stop the debug session and set a breakpoint instead.'; - this.sendEvent(new OutputEvent(message)); - this.sendErrorResponse(response, 1, message); - return Promise.resolve(); - } - return super.pauseRequest(response, args); - } - - protected async disconnectRequest(response: DebugProtocol.DisconnectResponse): Promise { - try { - if (this.isRunning) { - if (process.platform === 'win32') { - // We cannot pause on Windows - this.arduinoBackend.kill(); - this.sendResponse(response); - return; - } - // Need to pause first - const waitPromise = new Promise(resolve => this.waitPaused = resolve); - this.gdb.pause(); - await waitPromise; - } - await this.gdb.sendGDBExit(); - this.sendResponse(response); - } catch (err) { - this.sendErrorResponse(response, 1, err.message); - } - } - - protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void { - try { - const frame: FrameVariableReference = { - type: 'frame', - frameHandle: args.frameId, - }; - // const pins: ObjectVariableReference = { - // type: "object", - // varobjName: "__pins", - // frameHandle: 42000, - // } - - response.body = { - scopes: [ - // new Scope('Pins', this.variableHandles.create(pins), false), - new Scope('Local', this.variableHandles.create(frame), false), - new Scope('Global', GLOBAL_HANDLE_ID, false), - // new Scope('Static', STATIC_HANDLES_START + parseInt(args.frameId as any, 10), false) - ], - }; - - this.sendResponse(response); - } catch (err) { - this.sendErrorResponse(response, 1, err.message); - } - } - - protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise { - try { - response.body = { - variables: [] as DebugProtocol.Variable[] - }; - const ref = this.variableHandles.get(args.variablesReference); - if (args.variablesReference === GLOBAL_HANDLE_ID) { - // Use hardcoded global handle to load and store global variables - response.body.variables = await this.variableHandler.getGlobalVariables(); - } else if (args.variablesReference >= STATIC_HANDLES_START && args.variablesReference <= STATIC_HANDLES_FINISH) { - // Use STATIC_HANDLES_START to shift the framehandles back - const frameHandle = args.variablesReference - STATIC_HANDLES_START; - response.body.variables = await this.variableHandler.getStaticVariables(frameHandle); - } else if (ref && ref.type === 'frame') { - // List variables for current frame - response.body.variables = await this.handleVariableRequestFrame(ref); - } else if (ref && ref.varobjName === '__pins') { - response.body.variables = await this.variableHandler.handlePinStatusRequest(); - } else if (ref && ref.type === 'object') { - // List data under any variable - response.body.variables = await this.handleVariableRequestObject(ref); - } - this.sendResponse(response); - } catch (err) { - this.sendErrorResponse(response, 1, err.message); - } - } - -} diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts deleted file mode 100644 index 0b0b4e92b..000000000 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts +++ /dev/null @@ -1,73 +0,0 @@ -import * as path from 'path'; -import * as fs from 'arduino-ide-extension/lib/node/fs-extra' -import { spawn } from 'child_process'; -import { GDBBackend } from 'cdt-gdb-adapter/dist/GDBBackend'; -import { MIFrameInfo } from 'cdt-gdb-adapter/dist/mi'; -import { ArduinoLaunchRequestArguments } from './arduino-debug-session'; -import { ArduinoParser } from './arduino-parser'; - -export class ArduinoGDBBackend extends GDBBackend { - - constructor() { - super(); - this.parser = new ArduinoParser(this); - } - - spawn(requestArgs: ArduinoLaunchRequestArguments): Promise { - if (!requestArgs.sketch) { - throw new Error('Missing argument: sketch'); - } - if (!requestArgs.fqbn) { - throw new Error('Missing argument: fqbn') - } - const sketchFS = fs.statSync(requestArgs.sketch); - const sketchDir = sketchFS.isFile() ? path.dirname(requestArgs.sketch) : requestArgs.sketch; - const command = requestArgs.arduinoCli || 'arduino-cli'; - const args = [ - 'debug', - '-p', requestArgs.uploadPort || 'none', - '-b', requestArgs.fqbn, - '--interpreter', 'mi2', - sketchDir - ]; - console.log('Starting debugger:', command, JSON.stringify(args)); - const proc = spawn(command, args); - this.proc = proc; - this.out = proc.stdin; - return (this.parser as ArduinoParser).parseFull(proc); - } - - sendFileExecAndSymbols(): Promise { - // The program file is already sent by `arduino-cli` - return Promise.resolve(); - } - - sendExecInterrupt(threadId?: number) { - let command = '-exec-interrupt'; - if (threadId) { - command += ` --thread ${threadId}`; - } - return this.sendCommand(command); - } - - sendStackInfoFrame(threadId: number, frameId: number): Promise<{ frame: MIFrameInfo }> { - const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`; - return this.sendCommand(command); - } - - sendTargetDetach(): Promise { - return this.sendCommand('-target-detach'); - } - - kill(): void { - if (!this.proc) { - return; - } - if (process.platform === 'win32') { - spawn('taskkill', ['/pid', this.proc.pid.toString(), '/f', '/t']); - } else { - this.proc.kill('SIGKILL'); - } - } - -} diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts deleted file mode 100644 index 07767bafd..000000000 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { Readable } from 'stream'; -import { MIParser } from "cdt-gdb-adapter/dist/MIParser"; - -const LINE_REGEX = /(.*)(\r?\n)/; - -export class ArduinoParser extends MIParser { - - protected rejectReady?: (error: Error) => void; - - parseFull(proc: ChildProcessWithoutNullStreams): Promise { - return new Promise((resolve, reject) => { - // Detect errors when the child process could not be spawned - proc.on('error', reject); - - this.waitReady = () => { - this.rejectReady = undefined; - resolve(); - } - this.rejectReady = (error: Error) => { - this.waitReady = undefined; - reject(error); - } - // Detect unexpected termination - proc.on('exit', () => { - if (this.rejectReady) { - this.rejectReady(new Error('The gdb debugger terminated unexpectedly.')); - } - }); - this.readInputStream(proc.stdout); - this.readErrorStream(proc.stderr); - }); - } - - private readInputStream(stream: Readable) { - let buff = ''; - stream.on('data', chunk => { - buff += chunk.toString(); - let regexArray = LINE_REGEX.exec(buff); - while (regexArray) { - const line = regexArray[1]; - this.line = line; - this.pos = 0; - this.handleLine(); - // Detect error emitted as log message - if (this.rejectReady && line.toLowerCase().startsWith('&"error')) { - this.pos = 1; - this.rejectReady(new Error(this.handleCString() || regexArray[1])); - this.rejectReady = undefined; - } - buff = buff.substring(regexArray[1].length + regexArray[2].length); - regexArray = LINE_REGEX.exec(buff); - } - }); - } - - private readErrorStream(stream: Readable) { - let buff = ''; - stream.on('data', chunk => { - buff += chunk.toString(); - let regexArray = LINE_REGEX.exec(buff); - while (regexArray) { - const line = regexArray[1]; - this.gdb.emit('consoleStreamOutput', line + '\n', 'stderr'); - // Detect error emitted on the stderr stream - if (this.rejectReady && line.toLowerCase().startsWith('error')) { - this.rejectReady(new Error(line)); - this.rejectReady = undefined; - } - buff = buff.substring(regexArray[1].length + regexArray[2].length); - regexArray = LINE_REGEX.exec(buff); - } - }); - } - -} diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts deleted file mode 100644 index e440c9462..000000000 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as path from 'path'; -import { DebugProtocol } from "vscode-debugprotocol"; -import { Handles } from 'vscode-debugadapter/lib/handles'; -import { FrameReference, VariableReference } from "cdt-gdb-adapter/dist/GDBDebugSession"; -import { VarManager } from 'cdt-gdb-adapter/dist/varManager'; -import * as mi from 'cdt-gdb-adapter/dist/mi'; -import { ArduinoDebugSession } from "./arduino-debug-session"; -import { ArduinoGDBBackend } from './arduino-gdb-backend'; - -export class ArduinoVariableHandler { - - protected readonly gdb: ArduinoGDBBackend; - protected readonly varMgr: VarManager; - - protected globalHandle: number; - - constructor(protected readonly session: ArduinoDebugSession, - protected frameHandles: Handles, - protected variableHandles: Handles) { - this.gdb = session.arduinoBackend; - this.varMgr = new VarManager(this.gdb); - } - - createGlobalHandle() { - this.globalHandle = this.frameHandles.create({ - threadId: -1, - frameId: -1 - }); - } - - /** TODO */ - async getGlobalVariables(): Promise { - throw new Error('Global variables are not supported yet.'); - const frame = this.frameHandles.get(this.globalHandle); - const symbolInfo: any[] = [] // this.symbolTable.getGlobalVariables(); - const variables: DebugProtocol.Variable[] = []; - - for (const symbol of symbolInfo) { - const name = `global_var_${symbol.name}`; - const variable = await this.getVariables(frame, name, symbol.name, -1); - variables.push(variable); - } - - return variables; - } - - /** TODO */ - async getStaticVariables(frameHandle: number): Promise { - throw new Error('Static variables are not supported yet.'); - const frame = this.frameHandles.get(frameHandle); - const result = await this.gdb.sendStackInfoFrame(frame.threadId, frame.frameId); - const file = path.normalize(result.frame.file || ''); - const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file); - const variables: DebugProtocol.Variable[] = []; - - // Fetch stack depth to obtain frameId/threadId/depth tuple - const stackDepth = await mi.sendStackInfoDepth(this.gdb, { maxDepth: 100 }); - const depth = parseInt(stackDepth.depth, 10); - - for (const symbol of symbolInfo) { - const name = `${file}_static_var_${symbol.name}`; - const variable = await this.getVariables(frame, name, symbol.name, depth); - variables.push(variable); - } - - return variables; - } - - private async getVariables(frame: FrameReference, name: string, expression: string, depth: number): Promise { - let global = this.varMgr.getVar(frame.frameId, frame.threadId, depth, name); - - if (global) { - // Update value if it is already loaded - const vup = await mi.sendVarUpdate(this.gdb, { name }); - const update = vup.changelist[0]; - if (update && update.in_scope === 'true' && update.name === global.varname) { - global.value = update.value; - } - } else { - // create var in GDB and store it in the varMgr - const varCreateResponse = await mi.sendVarCreate(this.gdb, { - name, - frame: 'current', - expression, - }); - - global = this.varMgr.addVar(frame.frameId, frame.threadId, depth, name, true, false, varCreateResponse); - } - - return { - name: expression, - value: (global.value === void 0) ? '' : global.value, - type: global.type, - variablesReference: parseInt(global.numchild, 10) > 0 - ? this.variableHandles.create({ - frameHandle: this.globalHandle, - type: 'object', - varobjName: global.varname, - }) - : 0, - }; - } - - async handlePinStatusRequest(): Promise { - const variables: DebugProtocol.Variable[] = []; - variables.push({ - name: "D2", - type: "gpio", - value: "0x00", - variablesReference: 0 - }) - return variables; - } - -} diff --git a/arduino-debugger-extension/src/node/debug-adapter/main.ts b/arduino-debugger-extension/src/node/debug-adapter/main.ts deleted file mode 100644 index 59fa0ee97..000000000 --- a/arduino-debugger-extension/src/node/debug-adapter/main.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as process from 'process'; -import { logger } from 'vscode-debugadapter/lib/logger'; -import { ArduinoDebugSession } from './arduino-debug-session'; -import { DebugSession } from 'vscode-debugadapter'; - -process.on('uncaughtException', (err: any) => { - logger.error(JSON.stringify(err)); -}); - -DebugSession.run(ArduinoDebugSession); diff --git a/arduino-debugger-extension/tsconfig.json b/arduino-debugger-extension/tsconfig.json deleted file mode 100644 index 86907b52d..000000000 --- a/arduino-debugger-extension/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "noImplicitAny": true, - "noEmitOnError": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "strictNullChecks": true, - "experimentalDecorators": true, - "downlevelIteration": true, - "emitDecoratorMetadata": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es6", - "outDir": "lib", - "lib": [ - "es6", - "dom" - ], - "jsx": "react", - "sourceMap": true, - "skipLibCheck": true - }, - "include": [ - "src" - ], - "files": [ - "../node_modules/@theia/monaco/src/typings/monaco/index.d.ts" - ] -} \ No newline at end of file diff --git a/arduino-debugger-extension/tslint.json b/arduino-debugger-extension/tslint.json deleted file mode 100644 index 55b006287..000000000 --- a/arduino-debugger-extension/tslint.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [true, "check-space"], - "curly": false, - "forin": false, - "indent": [true, "spaces"], - "max-line-length": [true, 180], - "no-trailing-whitespace": false, - "no-unused-expression": true, - "no-var-keyword": true, - "one-line": [true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "radix": true, - "trailing-comma": [false], - "triple-equals": [true, "allow-null-check"], - "typedef-whitespace": [true, { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }], - "variable-name": false, - "whitespace": [true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} \ No newline at end of file diff --git a/arduino-ide-extension/README.md b/arduino-ide-extension/README.md index 701f2aed7..4f8794277 100644 --- a/arduino-ide-extension/README.md +++ b/arduino-ide-extension/README.md @@ -1,6 +1,6 @@ ## Arduino IDE Extension -Arduino Pro IDE is based on Theia, and most of its IDE features, UIs and customizations are implemented in this Theia extension. +Arduino IDE is based on Theia, and most of its IDE features, UIs and customizations are implemented in this Theia extension. ### IDE Services diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index ffaab8c33..79dc69787 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -1,6 +1,6 @@ { "name": "arduino-ide-extension", - "version": "0.1.3", + "version": "2.0.0-beta.1", "description": "An extension for Theia building the Arduino IDE", "license": "MIT", "scripts": { @@ -123,7 +123,7 @@ ], "arduino": { "cli": { - "version": "20210203" + "version": "0.15.2" } } } diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts index ff22d3f8f..665c9fea9 100644 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ b/arduino-ide-extension/src/browser/arduino-commands.ts @@ -20,11 +20,4 @@ export namespace ArduinoCommands { id: 'arduino-open-boards-dialog' }; - export const TOGGLE_ADVANCED_MODE: Command = { - id: 'arduino-toggle-advanced-mode' - }; - export const TOGGLE_ADVANCED_MODE_TOOLBAR: Command = { - id: 'arduino-toggle-advanced-mode-toolbar' - }; - } diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 7df51e81f..1e63e4bcc 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -269,12 +269,6 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut command: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR, tooltip: 'Serial Monitor' }); - registry.registerItem({ - id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id, - command: ArduinoCommands.TOGGLE_ADVANCED_MODE_TOOLBAR.id, - tooltip: this.editorMode.proMode ? 'Switch to Classic Mode' : 'Switch to Advanced Mode', - text: this.editorMode.proMode ? '$(toggle-on)' : '$(toggle-off)' - }); } registerCommands(registry: CommandRegistry): void { @@ -295,29 +289,19 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut } } }); - registry.registerCommand(ArduinoCommands.TOGGLE_ADVANCED_MODE, { - isToggled: () => this.editorMode.proMode, - execute: () => this.editorMode.toggleProMode() - }); - registry.registerCommand(ArduinoCommands.TOGGLE_ADVANCED_MODE_TOOLBAR, { - isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right', - isToggled: () => this.editorMode.proMode, - execute: () => this.editorMode.toggleProMode() - }); } registerMenus(registry: MenuModelRegistry) { - if (!this.editorMode.proMode) { - const menuId = (menuPath: string[]): string => { - const index = menuPath.length - 1; - const menuId = menuPath[index]; - return menuId; - } - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(MonacoMenus.SELECTION)); - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(EditorMainMenu.GO)); - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(TerminalMenus.TERMINAL)); - registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(CommonMenus.VIEW)); + const menuId = (menuPath: string[]): string => { + const index = menuPath.length - 1; + const menuId = menuPath[index]; + return menuId; } + registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(MonacoMenus.SELECTION)); + registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(EditorMainMenu.GO)); + registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(TerminalMenus.TERMINAL)); + registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(CommonMenus.VIEW)); + registry.registerSubmenu(ArduinoMenus.SKETCH, 'Sketch'); registry.registerSubmenu(ArduinoMenus.TOOLS, 'Tools'); registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { @@ -325,10 +309,6 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut label: 'Optimize for Debugging', order: '4' }); - registry.registerMenuAction(ArduinoMenus.HELP__CONTROL_GROUP, { - commandId: ArduinoCommands.TOGGLE_ADVANCED_MODE.id, - label: 'Advanced Mode' - }); } protected async openSketchFiles(uri: URI): Promise { @@ -386,7 +366,7 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut light: 'editorWidget.background', hc: 'editorWidget.background' }, - description: 'Color of the Arduino Pro IDE foreground which is used for dialogs, such as the Select Board dialog.' + description: 'Color of the Arduino IDE foreground which is used for dialogs, such as the Select Board dialog.' }, { id: 'arduino.toolbar.background', 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 0ef4fb5d2..7c72a3c4c 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -85,7 +85,7 @@ import { WorkspaceFrontendContribution, ArduinoFileMenuContribution } from './th import { Contribution } from './contributions/contribution'; import { NewSketch } from './contributions/new-sketch'; import { OpenSketch } from './contributions/open-sketch'; -import { CloseSketch } from './contributions/close-sketch'; +import { Close } from './contributions/close'; import { SaveAsSketch } from './contributions/save-as-sketch'; import { SaveSketch } from './contributions/save-sketch'; import { VerifySketch } from './contributions/verify-sketch'; @@ -140,6 +140,8 @@ import { bindArduinoPreferences } from './arduino-preferences' import { SettingsService, SettingsDialog, SettingsWidget, SettingsDialogProps } from './settings'; import { AddFile } from './contributions/add-file'; import { ArchiveSketch } from './contributions/archive-sketch'; +import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution'; +import { OutputToolbarContribution } from './theia/output/output-toolbar-contribution'; const ElementQueries = require('css-element-queries/src/ElementQueries'); @@ -313,6 +315,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ShellLayoutRestorer).toSelf().inSingletonScope(); rebind(TheiaShellLayoutRestorer).toService(ShellLayoutRestorer); + // No dropdown for the _Output_ view. + bind(OutputToolbarContribution).toSelf().inSingletonScope(); + rebind(TheiaOutputToolbarContribution).toService(OutputToolbarContribution); + bind(ArduinoDaemon).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ArduinoDaemonPath)).inSingletonScope(); // File-system extension @@ -326,7 +332,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, NewSketch); Contribution.configure(bind, OpenSketch); - Contribution.configure(bind, CloseSketch); + Contribution.configure(bind, Close); Contribution.configure(bind, SaveSketch); Contribution.configure(bind, SaveAsSketch); Contribution.configure(bind, VerifySketch); 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 e83e47b25..12771c4a9 100644 --- a/arduino-ide-extension/src/browser/boards/boards-data-store.ts +++ b/arduino-ide-extension/src/browser/boards/boards-data-store.ts @@ -157,7 +157,7 @@ export class BoardsDataStore implements FrontendApplicationContribution { } protected getStorageKey(fqbn: string, version: Installable.Version): string { - return `.arduinoProIDE-configOptions-${version}-${fqbn}`; + return `.arduinoIDE-configOptions-${version}-${fqbn}`; } protected async getBoardDetailsSafe(fqbn: string): Promise { diff --git a/arduino-ide-extension/src/browser/contributions/board-selection.ts b/arduino-ide-extension/src/browser/contributions/board-selection.ts index 57a7dc019..3373e20cf 100644 --- a/arduino-ide-extension/src/browser/contributions/board-selection.ts +++ b/arduino-ide-extension/src/browser/contributions/board-selection.ts @@ -2,12 +2,13 @@ import { inject, injectable } from 'inversify'; import { remote } from 'electron'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; +import { firstToUpperCase } from '../../common/utils'; import { BoardsConfig } from '../boards/boards-config'; import { MainMenuManager } from '../../common/main-menu-manager'; import { BoardsListWidget } from '../boards/boards-list-widget'; import { NotificationCenter } from '../notification-center'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; -import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus'; +import { ArduinoMenus, PlaceholderMenuNode, unregisterSubmenu } from '../menu/arduino-menus'; import { BoardsService, InstalledBoardWithPackage, AvailablePorts, Port } from '../../common/protocol'; import { SketchContribution, Command, CommandRegistry } from './contribution'; @@ -150,39 +151,61 @@ PID: ${PID}`; } // Installed ports - for (const address of Object.keys(availablePorts)) { - if (!!availablePorts[address]) { - const [port, boards] = availablePorts[address]; - if (!boards.length) { - boards.push({ - name: '' - }); - } - for (const { name, fqbn } of boards) { - const id = `arduino-select-port--${address}${fqbn ? `--${fqbn}` : ''}`; - const command = { id }; - const handler = { - execute: () => { - if (!Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort)) { - this.boardsServiceProvider.boardsConfig = { - selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard, - selectedPort: port + const registerPorts = (ports: AvailablePorts) => { + const addresses = Object.keys(ports); + if (!addresses.length) { + return; + } + + // Register placeholder for protocol + const [port] = ports[addresses[0]]; + const protocol = port.protocol; + const menuPath = [...portsSubmenuPath, protocol]; + const placeholder = new PlaceholderMenuNode(menuPath, `${firstToUpperCase(port.protocol)} ports`); + this.menuModelRegistry.registerMenuNode(menuPath, placeholder); + this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.menuModelRegistry.unregisterMenuNode(placeholder.id))); + + for (const address of addresses) { + if (!!ports[address]) { + const [port, boards] = ports[address]; + if (!boards.length) { + boards.push({ + name: '' + }); + } + for (const { name, fqbn } of boards) { + const id = `arduino-select-port--${address}${fqbn ? `--${fqbn}` : ''}`; + const command = { id }; + const handler = { + execute: () => { + if (!Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort)) { + this.boardsServiceProvider.boardsConfig = { + selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard, + selectedPort: port + } } - } - }, - isToggled: () => Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort) - }; - const menuAction = { - commandId: id, - label: `${address}${name ? ` (${name})` : ''}` - }; - this.commandRegistry.registerCommand(command, handler); - this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.commandRegistry.unregisterCommand(command))); - this.menuModelRegistry.registerMenuAction(portsSubmenuPath, menuAction); + }, + isToggled: () => Port.equals(port, this.boardsServiceProvider.boardsConfig.selectedPort) + }; + const label = `${address}${name ? ` (${name})` : ''}`; + const menuAction = { + commandId: id, + label, + order: `1${label}` // `1` comes after the placeholder which has order `0` + }; + this.commandRegistry.registerCommand(command, handler); + this.toDisposeBeforeMenuRebuild.push(Disposable.create(() => this.commandRegistry.unregisterCommand(command))); + this.menuModelRegistry.registerMenuAction(menuPath, menuAction); + } } } } + const { serial, network, unknown } = AvailablePorts.groupByProtocol(availablePorts); + registerPorts(serial); + registerPorts(network); + registerPorts(unknown); + this.mainMenuManager.update(); } diff --git a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts index e04e06ade..1b19be40c 100644 --- a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts +++ b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts @@ -53,7 +53,7 @@ export class BurnBootloader extends SketchContribution { this.preferences.get('arduino.upload.verify'), this.preferences.get('arduino.upload.verbose') ]); - this.outputChannelManager.getChannel('Arduino: bootloader').clear(); + this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.burnBootloader({ fqbn, programmer, diff --git a/arduino-ide-extension/src/browser/contributions/close-sketch.ts b/arduino-ide-extension/src/browser/contributions/close.ts similarity index 64% rename from arduino-ide-extension/src/browser/contributions/close-sketch.ts rename to arduino-ide-extension/src/browser/contributions/close.ts index 3eeda59a7..46bd72ceb 100644 --- a/arduino-ide-extension/src/browser/contributions/close-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/close.ts @@ -1,20 +1,50 @@ import { inject, injectable } from 'inversify'; +import { toArray } from '@phosphor/algorithm'; import { remote } from 'electron'; +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 { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, URI } from './contribution'; import { SaveAsSketch } from './save-as-sketch'; -import { EditorManager } from '@theia/editor/lib/browser'; -import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry, URI } from './contribution'; +/** + * Closes the `current` closeable editor, or any closeable current widget from the main area, or the current sketch window. + */ @injectable() -export class CloseSketch extends SketchContribution { +export class Close extends SketchContribution { @inject(EditorManager) protected readonly editorManager: EditorManager; + protected shell: ApplicationShell; + + onStart(app: FrontendApplication): void { + this.shell = app.shell; + } + registerCommands(registry: CommandRegistry): void { - registry.registerCommand(CloseSketch.Commands.CLOSE_SKETCH, { + 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; @@ -48,7 +78,7 @@ export class CloseSketch extends SketchContribution { registerMenus(registry: MenuModelRegistry): void { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { - commandId: CloseSketch.Commands.CLOSE_SKETCH.id, + commandId: Close.Commands.CLOSE.id, label: 'Close', order: '5' }); @@ -56,7 +86,7 @@ export class CloseSketch extends SketchContribution { registerKeybindings(registry: KeybindingRegistry): void { registry.registerKeybinding({ - command: CloseSketch.Commands.CLOSE_SKETCH.id, + command: Close.Commands.CLOSE.id, keybinding: 'CtrlCmd+W' }); } @@ -80,10 +110,10 @@ export class CloseSketch extends SketchContribution { } -export namespace CloseSketch { +export namespace Close { export namespace Commands { - export const CLOSE_SKETCH: Command = { - id: 'arduino-close-sketch' + export const CLOSE: Command = { + id: 'arduino-close' }; } } diff --git a/arduino-ide-extension/src/browser/contributions/examples.ts b/arduino-ide-extension/src/browser/contributions/examples.ts index 960bc2319..4b1d5f1a5 100644 --- a/arduino-ide-extension/src/browser/contributions/examples.ts +++ b/arduino-ide-extension/src/browser/contributions/examples.ts @@ -3,12 +3,13 @@ import { inject, injectable, postConstruct } from 'inversify'; import { MenuPath, CompositeMenuNode } from '@theia/core/lib/common/menu'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { OpenSketch } from './open-sketch'; -import { ArduinoMenus } from '../menu/arduino-menus'; +import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { MainMenuManager } from '../../common/main-menu-manager'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { ExamplesService, ExampleContainer } from '../../common/protocol/examples-service'; import { SketchContribution, CommandRegistry, MenuModelRegistry } from './contribution'; import { NotificationCenter } from '../notification-center'; +import { Board } from '../../common/protocol'; @injectable() export abstract class Examples extends SketchContribution { @@ -32,10 +33,10 @@ export abstract class Examples extends SketchContribution { @postConstruct() init(): void { - this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.handleBoardChanged(selectedBoard?.fqbn)); + this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.handleBoardChanged(selectedBoard)); } - protected handleBoardChanged(fqbn: string | undefined): void { + protected handleBoardChanged(board: Board | undefined): void { // NOOP } @@ -58,27 +59,33 @@ export abstract class Examples extends SketchContribution { } registerRecursively( - exampleContainer: ExampleContainer, + exampleContainerOrPlaceholder: ExampleContainer | string, menuPath: MenuPath, pushToDispose: DisposableCollection = new DisposableCollection()): void { - const { label, sketches, children } = exampleContainer; - const submenuPath = [...menuPath, label]; - this.menuRegistry.registerSubmenu(submenuPath, label); - children.forEach(child => this.registerRecursively(child, submenuPath, pushToDispose)); - for (const sketch of sketches) { - const { uri } = sketch; - const commandId = `arduino-open-example-${submenuPath.join(':')}--${uri}`; - const command = { id: commandId }; - const handler = { - execute: async () => { - const sketch = await this.sketchService.cloneExample(uri); - this.commandService.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch); - } - }; - pushToDispose.push(this.commandRegistry.registerCommand(command, handler)); - this.menuRegistry.registerMenuAction(submenuPath, { commandId, label: sketch.name }); - pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(command))); + if (typeof exampleContainerOrPlaceholder === 'string') { + const placeholder = new PlaceholderMenuNode(menuPath, exampleContainerOrPlaceholder); + this.menuRegistry.registerMenuNode(menuPath, placeholder); + pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuNode(placeholder.id))); + } else { + const { label, sketches, children } = exampleContainerOrPlaceholder; + const submenuPath = [...menuPath, label]; + this.menuRegistry.registerSubmenu(submenuPath, label); + children.forEach(child => this.registerRecursively(child, submenuPath, pushToDispose)); + for (const sketch of sketches) { + const { uri } = sketch; + const commandId = `arduino-open-example-${submenuPath.join(':')}--${uri}`; + const command = { id: commandId }; + const handler = { + execute: async () => { + const sketch = await this.sketchService.cloneExample(uri); + this.commandService.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch); + } + }; + pushToDispose.push(this.commandRegistry.registerCommand(command, handler)); + this.menuRegistry.registerMenuAction(submenuPath, { commandId, label: sketch.name }); + pushToDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(command))); + } } } @@ -101,10 +108,12 @@ export class BuiltInExamples extends Examples { return; } this.toDispose.dispose(); - for (const container of exampleContainers) { + for (const container of ['Built-in examples', ...exampleContainers]) { this.registerRecursively(container, ArduinoMenus.EXAMPLES__BUILT_IN_GROUP, this.toDispose); } this.menuManager.update(); + // TODO: remove + console.log(typeof this.menuRegistry); } } @@ -123,17 +132,27 @@ export class LibraryExamples extends Examples { this.notificationCenter.onLibraryUninstalled(() => this.register()); } - protected handleBoardChanged(fqbn: string | undefined): void { - this.register(fqbn); + protected handleBoardChanged(board: Board | undefined): void { + this.register(board); } - protected async register(fqbn: string | undefined = this.boardsServiceClient.boardsConfig.selectedBoard?.fqbn) { + protected async register(board: Board | undefined = this.boardsServiceClient.boardsConfig.selectedBoard) { return this.queue.add(async () => { this.toDispose.dispose(); - if (!fqbn) { + if (!board || !board.fqbn) { return; } + const { fqbn, name } = board; const { user, current, any } = await this.examplesService.installed({ fqbn }); + if (user.length) { + (user as any).unshift('Examples from Custom Libraries'); + } + if (current.length) { + (current as any).unshift(`Examples for ${name}`); + } + if (any.length) { + (any as any).unshift('Examples for any board'); + } for (const container of user) { this.registerRecursively(container, ArduinoMenus.EXAMPLES__USER_LIBS_GROUP, this.toDispose); } diff --git a/arduino-ide-extension/src/browser/contributions/include-library.ts b/arduino-ide-extension/src/browser/contributions/include-library.ts index 6ad8756a1..079e6b2ac 100644 --- a/arduino-ide-extension/src/browser/contributions/include-library.ts +++ b/arduino-ide-extension/src/browser/contributions/include-library.ts @@ -5,8 +5,8 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { EditorManager } from '@theia/editor/lib/browser'; import { MenuModelRegistry, MenuPath } from '@theia/core/lib/common/menu'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { ArduinoMenus } from '../menu/arduino-menus'; -import { LibraryPackage, LibraryLocation, LibraryService } from '../../common/protocol'; +import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; +import { LibraryPackage, LibraryService } from '../../common/protocol'; import { MainMenuManager } from '../../common/main-menu-manager'; import { LibraryListWidget } from '../library/library-list-widget'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; @@ -84,19 +84,35 @@ export class IncludeLibrary extends SketchContribution { // `Arduino libraries` const packageMenuPath = [...includeLibMenuPath, '2_arduino']; const userMenuPath = [...includeLibMenuPath, '3_contributed']; - for (const library of libraries) { - this.toDispose.push(this.registerLibrary(library, library.location === LibraryLocation.USER ? userMenuPath : packageMenuPath)); + const { user, rest } = LibraryPackage.groupByLocation(libraries); + if (rest.length) { + (rest as any).unshift('Arduino libraries'); + } + if (user.length) { + (user as any).unshift('Contributed libraries'); + } + + for (const library of user) { + this.toDispose.push(this.registerLibrary(library, userMenuPath)); + } + for (const library of rest) { + this.toDispose.push(this.registerLibrary(library, packageMenuPath)); } this.mainMenuManager.update(); }); } - protected registerLibrary(library: LibraryPackage, menuPath: MenuPath): Disposable { - const commandId = `arduino-include-library--${library.name}:${library.author}`; + protected registerLibrary(libraryOrPlaceholder: LibraryPackage | string, menuPath: MenuPath): Disposable { + if (typeof libraryOrPlaceholder === 'string') { + const placeholder = new PlaceholderMenuNode(menuPath, libraryOrPlaceholder); + this.menuRegistry.registerMenuNode(menuPath, placeholder); + return Disposable.create(() => this.menuRegistry.unregisterMenuNode(placeholder.id)); + } + const commandId = `arduino-include-library--${libraryOrPlaceholder.name}:${libraryOrPlaceholder.author}`; const command = { id: commandId }; - const handler = { execute: () => this.commandRegistry.executeCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY.id, library) }; - const menuAction = { commandId, label: library.name }; + const handler = { execute: () => this.commandRegistry.executeCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY.id, libraryOrPlaceholder) }; + const menuAction = { commandId, label: libraryOrPlaceholder.name }; this.menuRegistry.registerMenuAction(menuPath, menuAction); return new DisposableCollection( this.commandRegistry.registerCommand(command, handler), diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index 06a1fd6e8..d8cb8c317 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -122,7 +122,7 @@ export class UploadSketch extends SketchContribution { verify }; } - this.outputChannelManager.getChannel('Arduino: upload').clear(); + this.outputChannelManager.getChannel('Arduino').clear(); if (usingProgrammer) { await this.coreService.uploadUsingProgrammer(options); } else { diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index 606b4095a..01bc2a1cb 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -77,7 +77,7 @@ export class VerifySketch extends SketchContribution { const { boardsConfig } = this.boardsServiceClientImpl; const fqbn = await this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn); const verbose = this.preferences.get('arduino.compile.verbose'); - this.outputChannelManager.getChannel('Arduino: compile').clear(); + this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.compile({ sketchUri: uri, fqbn, diff --git a/arduino-ide-extension/src/browser/editor-mode.ts b/arduino-ide-extension/src/browser/editor-mode.ts index bd3df9464..28ee63425 100644 --- a/arduino-ide-extension/src/browser/editor-mode.ts +++ b/arduino-ide-extension/src/browser/editor-mode.ts @@ -1,10 +1,6 @@ import { injectable, inject } from 'inversify'; -import { ApplicationShell, FrontendApplicationContribution, FrontendApplication, Widget } from '@theia/core/lib/browser'; -import { EditorWidget } from '@theia/editor/lib/browser'; -import { OutputWidget } from '@theia/output/lib/browser/output-widget'; +import { FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser'; import { MainMenuManager } from '../common/main-menu-manager'; -import { BoardsListWidget } from './boards/boards-list-widget'; -import { LibraryListWidget } from './library/library-list-widget'; @injectable() export class EditorMode implements FrontendApplicationContribution { @@ -16,41 +12,6 @@ export class EditorMode implements FrontendApplicationContribution { onStart(app: FrontendApplication): void { this.app = app; - if (this.proMode) { - // We use this CSS class on the body to modify the visibility of the close button for the editors and views. - document.body.classList.add(EditorMode.PRO_MODE_KEY); - } - } - - get proMode(): boolean { - const value = window.localStorage.getItem(EditorMode.PRO_MODE_KEY); - return value === 'true'; - } - - async toggleProMode(): Promise { - const oldState = this.proMode; - const inAdvancedMode = !oldState; - window.localStorage.setItem(EditorMode.PRO_MODE_KEY, String(inAdvancedMode)); - if (!inAdvancedMode) { - const { shell } = this.app; - // Close all widgets that are neither editor nor `Output` / `Boards Manager` / `Library Manager`. - for (const area of ['left', 'right', 'bottom', 'main'] as Array) { - shell.closeTabs(area, title => !this.isInSimpleMode(title.owner)); - } - } - // `storeLayout` has a sync API but the implementation is async, we store the layout manually before we reload the page. - // See: https://github.com/eclipse-theia/theia/issues/6579 - // XXX: hack instead of injecting the `ArduinoShellLayoutRestorer` we have to retrieve it from the application to avoid DI cycle. - const layoutRestorer = (this.app as any).layoutRestorer as { storeLayoutAsync(app: FrontendApplication): Promise }; - await layoutRestorer.storeLayoutAsync(this.app); - window.location.reload(true); - } - - protected isInSimpleMode(widget: Widget): boolean { - return widget instanceof EditorWidget - || widget instanceof OutputWidget - || widget instanceof BoardsListWidget - || widget instanceof LibraryListWidget; } get compileForDebug(): boolean { @@ -68,6 +29,5 @@ export class EditorMode implements FrontendApplicationContribution { } export namespace EditorMode { - export const PRO_MODE_KEY = 'arduino-advanced-mode'; export const COMPILE_FOR_DEBUG_KEY = 'arduino-compile-for-debug'; } diff --git a/arduino-ide-extension/src/browser/menu/arduino-menus.ts b/arduino-ide-extension/src/browser/menu/arduino-menus.ts index 6a61fbc67..6ef51e951 100644 --- a/arduino-ide-extension/src/browser/menu/arduino-menus.ts +++ b/arduino-ide-extension/src/browser/menu/arduino-menus.ts @@ -1,6 +1,6 @@ import { isOSX } from '@theia/core/lib/common/os'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; -import { MAIN_MENU_BAR, MenuModelRegistry, MenuNode } from '@theia/core/lib/common/menu'; +import { MAIN_MENU_BAR, MenuModelRegistry, MenuNode, MenuPath, SubMenuOptions } from '@theia/core/lib/common/menu'; export namespace ArduinoMenus { @@ -99,3 +99,24 @@ export function unregisterSubmenu(menuPath: string[], menuRegistry: MenuModelReg } (parent.children as Array).splice(index, 1); } + +/** + * Special menu node that is not backed by any commands and is always disabled. + */ +export class PlaceholderMenuNode implements MenuNode { + + constructor(protected readonly menuPath: MenuPath, readonly label: string, protected options: SubMenuOptions = { order: '0' }) { } + + get icon(): string | undefined { + return this.options?.iconClass; + } + + get sortString(): string { + return this.options?.order || this.label; + } + + get id(): string { + return [...this.menuPath, 'placeholder'].join('-'); + } + +} diff --git a/arduino-ide-extension/src/browser/output-service-impl.ts b/arduino-ide-extension/src/browser/output-service-impl.ts index c0ebe34d1..098db8b42 100644 --- a/arduino-ide-extension/src/browser/output-service-impl.ts +++ b/arduino-ide-extension/src/browser/output-service-impl.ts @@ -13,16 +13,10 @@ export class OutputServiceImpl implements OutputService { protected outputChannelManager: OutputChannelManager; append(message: OutputMessage): void { - const { name, chunk } = message; - const channel = this.outputChannelManager.getChannel(`Arduino: ${name}`); - // Zen-mode: we do not reveal the output for daemon messages. - const show: Promise = name === 'daemon' - // This will open and reveal the view but won't show it. You will see the toggle bottom panel on the status bar. - ? this.outputContribution.openView({ activate: false, reveal: false }) - // This will open, reveal but do not activate the Output view. - : Promise.resolve(channel.show({ preserveFocus: true })); - - show.then(() => channel.append(chunk)); + const { chunk } = message; + const channel = this.outputChannelManager.getChannel(`Arduino`); + channel.show({ preserveFocus: true }); + channel.append(chunk); } } diff --git a/arduino-ide-extension/src/browser/settings.tsx b/arduino-ide-extension/src/browser/settings.tsx index 2c1b2ba89..b856ea930 100644 --- a/arduino-ide-extension/src/browser/settings.tsx +++ b/arduino-ide-extension/src/browser/settings.tsx @@ -300,7 +300,7 @@ export class SettingsComponent extends React.Component diff --git a/arduino-ide-extension/src/browser/style/boards-config-dialog.css b/arduino-ide-extension/src/browser/style/boards-config-dialog.css index 7b15af3b8..04f3d341c 100644 --- a/arduino-ide-extension/src/browser/style/boards-config-dialog.css +++ b/arduino-ide-extension/src/browser/style/boards-config-dialog.css @@ -135,18 +135,6 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i { width: 740px; } -button.theia-button { - height: 31px; -} - -button.theia-button.secondary { - background-color: var(--theia-secondaryButton-background); - color: var(--theia-foreground); -} - -button.theia-button.main { - color: var(--theia-button-foreground); -} .dialogControl { margin: 0 20px 30px 0; diff --git a/arduino-ide-extension/src/browser/style/index.css b/arduino-ide-extension/src/browser/style/index.css index b44b7912a..051eac119 100644 --- a/arduino-ide-extension/src/browser/style/index.css +++ b/arduino-ide-extension/src/browser/style/index.css @@ -36,3 +36,28 @@ color: var(--theia-warningForeground); background-color: var(--theia-warningBackground); } + +/* Overrule the default Theia CSS button styles. */ + +button.theia-button, +.theia-button { + border: 1px solid var(--theia-dropdown-border); +} + +button.theia-button:hover, +.theia-button:hover { + border: 1px solid var(--theia-focusBorder); +} + +button.theia-button { + height: 31px; +} + +button.theia-button.secondary { + background-color: var(--theia-secondaryButton-background); + color: var(--theia-foreground); +} + +button.theia-button.main { + color: var(--theia-button-foreground); +} diff --git a/arduino-ide-extension/src/browser/theia/core/application-shell.ts b/arduino-ide-extension/src/browser/theia/core/application-shell.ts index dc4a89fca..e11917b6a 100644 --- a/arduino-ide-extension/src/browser/theia/core/application-shell.ts +++ b/arduino-ide-extension/src/browser/theia/core/application-shell.ts @@ -7,16 +7,12 @@ import { OutputWidget } from '@theia/output/lib/browser/output-widget'; import { ConnectionStatusService, ConnectionStatus } from '@theia/core/lib/browser/connection-status-service'; import { ApplicationShell as TheiaApplicationShell, Widget } from '@theia/core/lib/browser'; import { Sketch } from '../../../common/protocol'; -import { EditorMode } from '../../editor-mode'; import { SaveAsSketch } from '../../contributions/save-as-sketch'; import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl'; @injectable() export class ApplicationShell extends TheiaApplicationShell { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - @inject(CommandService) protected readonly commandService: CommandService; @@ -34,7 +30,7 @@ export class ApplicationShell extends TheiaApplicationShell { if (widget instanceof OutputWidget) { widget.title.closable = false; // TODO: https://arduino.slack.com/archives/C01698YT7S4/p1598011990133700 } - if (!this.editorMode.proMode && widget instanceof EditorWidget) { + if (widget instanceof EditorWidget) { // Make the editor un-closeable asynchronously. this.sketchesServiceClient.currentSketch().then(sketch => { if (sketch) { diff --git a/arduino-ide-extension/src/browser/theia/markers/problem-contribution.ts b/arduino-ide-extension/src/browser/theia/markers/problem-contribution.ts index 941866405..90647eca6 100644 --- a/arduino-ide-extension/src/browser/theia/markers/problem-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/markers/problem-contribution.ts @@ -1,26 +1,18 @@ -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import { KeybindingRegistry } from '@theia/core/lib/browser'; import { ProblemStat } from '@theia/markers/lib/browser/problem/problem-manager'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { ProblemContribution as TheiaProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution'; -import { EditorMode } from '../../editor-mode'; @injectable() export class ProblemContribution extends TheiaProblemContribution { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - async initializeLayout(app: FrontendApplication): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(app); - } + // NOOP } protected setStatusBarElement(problemStat: ProblemStat): void { - if (this.editorMode.proMode) { - super.setStatusBarElement(problemStat); - } + // NOOP } registerKeybindings(keybindings: KeybindingRegistry): void { diff --git a/arduino-ide-extension/src/browser/theia/navigator/navigator-contribution.ts b/arduino-ide-extension/src/browser/theia/navigator/navigator-contribution.ts index 582260583..169b25008 100644 --- a/arduino-ide-extension/src/browser/theia/navigator/navigator-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/navigator/navigator-contribution.ts @@ -1,20 +1,14 @@ -import { injectable, inject } from 'inversify'; +import { injectable } from 'inversify'; import { WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands'; import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { FileNavigatorContribution as TheiaFileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution'; -import { EditorMode } from '../../editor-mode'; @injectable() export class FileNavigatorContribution extends TheiaFileNavigatorContribution { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - async initializeLayout(app: FrontendApplication): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(app); - } + // NOOP } registerKeybindings(registry: KeybindingRegistry): void { diff --git a/arduino-ide-extension/src/browser/theia/outline/outline-contribution.ts b/arduino-ide-extension/src/browser/theia/outline/outline-contribution.ts index a94597ba0..a09c5b5fa 100644 --- a/arduino-ide-extension/src/browser/theia/outline/outline-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/outline/outline-contribution.ts @@ -1,14 +1,10 @@ -import { injectable, inject } from 'inversify'; +import { injectable } from 'inversify'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { OutlineViewContribution as TheiaOutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution'; -import { EditorMode } from '../../editor-mode'; @injectable() export class OutlineViewContribution extends TheiaOutlineViewContribution { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - constructor() { super(); this.options.defaultWidgetOptions = { @@ -18,9 +14,7 @@ export class OutlineViewContribution extends TheiaOutlineViewContribution { } async initializeLayout(app: FrontendApplication): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(app); - } + // NOOP } } diff --git a/arduino-ide-extension/src/browser/theia/output/output-toolbar-contribution.ts b/arduino-ide-extension/src/browser/theia/output/output-toolbar-contribution.ts new file mode 100644 index 000000000..54def6192 --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/output/output-toolbar-contribution.ts @@ -0,0 +1,15 @@ +import { injectable } from 'inversify'; +import { ReactTabBarToolbarItem, TabBarToolbarItem, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution'; + +@injectable() +export class OutputToolbarContribution extends TheiaOutputToolbarContribution { + + async registerToolbarItems(registry: TabBarToolbarRegistry): Promise { + await super.registerToolbarItems(registry); // Why is it async? + // It's a hack. Currently, it's not possible to unregister a toolbar contribution via API. + ((registry as any).items as Map).delete('channels'); + (registry as any).fireOnDidChange(); + } + +} diff --git a/arduino-ide-extension/src/browser/theia/scm/scm-contribution.ts b/arduino-ide-extension/src/browser/theia/scm/scm-contribution.ts index 30758d16e..60ddcf317 100644 --- a/arduino-ide-extension/src/browser/theia/scm/scm-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/scm/scm-contribution.ts @@ -1,24 +1,16 @@ -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import { ScmContribution as TheiaScmContribution } from '@theia/scm/lib/browser/scm-contribution'; import { StatusBarEntry } from '@theia/core/lib/browser/status-bar/status-bar'; -import { EditorMode } from '../../editor-mode'; @injectable() export class ScmContribution extends TheiaScmContribution { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - async initializeLayout(): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(); - } + // NOOP } protected setStatusBarEntry(id: string, entry: StatusBarEntry): void { - if (this.editorMode.proMode) { - super.setStatusBarEntry(id, entry); - } + // NOOP } } diff --git a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-frontend-contribution.ts b/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-frontend-contribution.ts index e8f0cea77..762512818 100644 --- a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-frontend-contribution.ts @@ -1,20 +1,14 @@ -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution, SearchInWorkspaceCommands } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution'; -import { EditorMode } from '../../editor-mode'; @injectable() export class SearchInWorkspaceFrontendContribution extends TheiaSearchInWorkspaceFrontendContribution { - @inject(EditorMode) - protected readonly editorMode: EditorMode; - async initializeLayout(app: FrontendApplication): Promise { - if (this.editorMode.proMode) { - return super.initializeLayout(app); - } + // NOOP } registerMenus(registry: MenuModelRegistry): void { diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts index a1ede3a1d..218b588b2 100644 --- a/arduino-ide-extension/src/common/protocol/boards-service.ts +++ b/arduino-ide-extension/src/common/protocol/boards-service.ts @@ -5,6 +5,25 @@ import { Installable } from './installable'; import { ArduinoComponent } from './arduino-component'; export type AvailablePorts = Record]>; +export namespace AvailablePorts { + export function groupByProtocol(availablePorts: AvailablePorts): { serial: AvailablePorts, network: AvailablePorts, unknown: AvailablePorts } { + const serial: AvailablePorts = {}; + const network: AvailablePorts = {}; + const unknown: AvailablePorts = {}; + for (const key of Object.keys(availablePorts)) { + const [port, boards] = availablePorts[key]; + const { protocol } = port; + if (protocol === 'serial') { + serial[key] = [port, boards]; + } else if (protocol === 'network') { + network[key] = [port, boards]; + } else { + unknown[key] = [port, boards]; + } + } + return { serial, network, unknown }; + } +} export interface AttachedBoardsChangeEvent { readonly oldState: Readonly<{ boards: Board[], ports: Port[] }>; diff --git a/arduino-ide-extension/src/common/protocol/library-service.ts b/arduino-ide-extension/src/common/protocol/library-service.ts index 04bb4ee6b..efbb37d64 100644 --- a/arduino-ide-extension/src/common/protocol/library-service.ts +++ b/arduino-ide-extension/src/common/protocol/library-service.ts @@ -69,4 +69,17 @@ export namespace LibraryPackage { return left.name === right.name && left.author === right.author; } + export function groupByLocation(packages: LibraryPackage[]): { user: LibraryPackage[], rest: LibraryPackage[] } { + const user: LibraryPackage[] = []; + const rest: LibraryPackage[] = []; + for (const pkg of packages) { + if (pkg.location === LibraryLocation.USER) { + user.push(pkg); + } else { + rest.push(pkg); + } + } + return { user, rest }; + } + } diff --git a/arduino-ide-extension/src/common/protocol/output-service.ts b/arduino-ide-extension/src/common/protocol/output-service.ts index f8e0cba2b..84509f679 100644 --- a/arduino-ide-extension/src/common/protocol/output-service.ts +++ b/arduino-ide-extension/src/common/protocol/output-service.ts @@ -1,5 +1,4 @@ export interface OutputMessage { - readonly name: string; readonly chunk: string; readonly severity?: 'error' | 'warning' | 'info'; // Currently not used! } diff --git a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts index a440b2a79..0341dc45c 100644 --- a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts +++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts @@ -1,8 +1,9 @@ import { injectable } from 'inversify' import { remote } from 'electron'; import { Keybinding } from '@theia/core/lib/common/keybinding'; -import { ElectronMainMenuFactory as TheiaElectronMainMenuFactory } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory'; -import { ArduinoMenus } from '../../../browser/menu/arduino-menus'; +import { CompositeMenuNode } from '@theia/core/lib/common/menu'; +import { ElectronMainMenuFactory as TheiaElectronMainMenuFactory, ElectronMenuOptions } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory'; +import { ArduinoMenus, PlaceholderMenuNode } from '../../../browser/menu/arduino-menus'; @injectable() export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory { @@ -23,7 +24,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory { protected createOSXMenu(): Electron.MenuItemConstructorOptions { const { submenu } = super.createOSXMenu(); - const label = 'Arduino Pro IDE'; + const label = 'Arduino IDE'; if (!!submenu && !(submenu instanceof remote.Menu)) { const [/* about */, /* settings */, ...rest] = submenu; const about = this.fillMenuTemplate([], this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP)); @@ -42,4 +43,15 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory { return { label, submenu }; } + protected handleDefault(menuNode: CompositeMenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] { + if (menuNode instanceof PlaceholderMenuNode) { + return [{ + label: menuNode.label, + enabled: false, + visible: true + }]; + } + return []; + } + } diff --git a/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts b/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts index 5f4a43e08..f1b0f639a 100644 --- a/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts +++ b/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts @@ -1,15 +1,20 @@ import { ContainerModule } from 'inversify'; import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory'; import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler'; +import { ElectronMainWindowService } from '@theia/core/lib/electron-common/electron-main-window-service'; import { ElectronMainApplication as TheiaElectronMainApplication } from '@theia/core/lib/electron-main/electron-main-application'; import { SplashService, splashServicePath } from '../electron-common/splash-service'; import { SplashServiceImpl } from './splash/splash-service-impl'; import { ElectronMainApplication } from './theia/electron-main-application'; +import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ElectronMainApplication).toSelf().inSingletonScope(); rebind(TheiaElectronMainApplication).toService(ElectronMainApplication); + bind(ElectronMainWindowServiceImpl).toSelf().inSingletonScope(); + rebind(ElectronMainWindowService).toService(ElectronMainWindowServiceImpl); + bind(SplashServiceImpl).toSelf().inSingletonScope(); bind(SplashService).toService(SplashServiceImpl); bind(ElectronConnectionHandler).toDynamicValue(context => diff --git a/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts b/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts index c096daf69..f3727aa73 100644 --- a/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts +++ b/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts @@ -13,7 +13,7 @@ import { SplashServiceImpl } from '../splash/splash-service-impl'; @injectable() export class ElectronMainApplication extends TheiaElectronMainApplication { - protected windows: BrowserWindow[] = []; + protected _windows: BrowserWindow[] = []; @inject(SplashServiceImpl) protected readonly splashService: SplashServiceImpl; @@ -34,7 +34,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { async createWindow(asyncOptions: MaybePromise = this.getDefaultBrowserWindowOptions()): Promise { const options = await asyncOptions; let electronWindow: BrowserWindow | undefined; - if (this.windows.length) { + if (this._windows.length) { electronWindow = new BrowserWindow(options); } else { const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); @@ -63,14 +63,14 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { splashScreenOpts }, this.splashService.onCloseRequested); } - this.windows.push(electronWindow); + this._windows.push(electronWindow); electronWindow.on('closed', () => { if (electronWindow) { - const index = this.windows.indexOf(electronWindow); + const index = this._windows.indexOf(electronWindow); if (index === -1) { console.warn(`Could not dispose browser window: '${electronWindow.title}'.`); } else { - this.windows.splice(index, 1); + this._windows.splice(index, 1); electronWindow = undefined; } } @@ -148,4 +148,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { } } + get windows(): BrowserWindow[] { + return this._windows.slice(); + } + } diff --git a/arduino-ide-extension/src/electron-main/theia/electron-main-window-service.ts b/arduino-ide-extension/src/electron-main/theia/electron-main-window-service.ts new file mode 100644 index 000000000..7a1428abc --- /dev/null +++ b/arduino-ide-extension/src/electron-main/theia/electron-main-window-service.ts @@ -0,0 +1,24 @@ +import { inject, injectable } from 'inversify'; +import { NewWindowOptions } from '@theia/core/lib/browser/window/window-service'; +import { ElectronMainWindowServiceImpl as TheiaElectronMainWindowService } from '@theia/core/lib/electron-main/electron-main-window-service-impl'; +import { ElectronMainApplication } from './electron-main-application'; + +@injectable() +export class ElectronMainWindowServiceImpl extends TheiaElectronMainWindowService { + + @inject(ElectronMainApplication) + protected readonly app: ElectronMainApplication; + + openNewWindow(url: string, { external }: NewWindowOptions): undefined { + if (!external) { + const existing = this.app.windows.find(window => window.webContents.getURL() === url); + if (existing) { + existing.focus(); + return; + } + } + return super.openNewWindow(url, { external }); + } + + +} diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index 27bee4d27..052e6ee10 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -271,7 +271,7 @@ export class BoardsServiceImpl implements BoardsService { resp.on('data', (r: PlatformInstallResp) => { const prog = r.getProgress(); if (prog && prog.getFile()) { - this.outputService.append({ name: 'board download', chunk: `downloading ${prog.getFile()}\n` }); + this.outputService.append({ chunk: `downloading ${prog.getFile()}\n` }); } }); await new Promise((resolve, reject) => { @@ -302,7 +302,7 @@ export class BoardsServiceImpl implements BoardsService { const resp = client.platformUninstall(req); resp.on('data', (_: PlatformUninstallResp) => { if (!logged) { - this.outputService.append({ name: 'board uninstall', chunk: `uninstalling ${item.id}\n` }); + this.outputService.append({ chunk: `uninstalling ${item.id}\n` }); logged = true; } }) diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 801b869df..a2024c694 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -24,7 +24,7 @@ export class CoreServiceImpl implements CoreService { protected readonly notificationService: NotificationServiceServer; async compile(options: CoreService.Compile.Options & { exportBinaries: boolean }): Promise { - this.outputService.append({ name: 'compile', chunk: 'Compile...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' }); + this.outputService.append({ chunk: 'Compile...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' }); const { sketchUri, fqbn } = options; const sketchFilePath = FileUri.fsPath(sketchUri); const sketchpath = dirname(sketchFilePath); @@ -48,15 +48,15 @@ export class CoreServiceImpl implements CoreService { try { await new Promise((resolve, reject) => { result.on('data', (cr: CompileResp) => { - this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() }); - this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(cr.getOutStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(cr.getErrStream_asU8()).toString() }); }); result.on('error', error => reject(error)); result.on('end', () => resolve()); }); - this.outputService.append({ name: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' }); + this.outputService.append({ chunk: '\n--------------------------\nCompilation complete.\n' }); } catch (e) { - this.outputService.append({ name: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' }); + this.outputService.append({ chunk: `Compilation error: ${e}\n`, severity: 'error' }); throw e; } } @@ -77,7 +77,7 @@ export class CoreServiceImpl implements CoreService { await this.compile(Object.assign(options, { exportBinaries: false })); const chunk = firstToUpperCase(task) + '...\n'; - this.outputService.append({ name: 'upload', chunk: chunk + JSON.stringify(options, null, 2) + '\n--------------------------\n' }); + this.outputService.append({ chunk: chunk + JSON.stringify(options, null, 2) + '\n--------------------------\n' }); const { sketchUri, fqbn, port, programmer } = options; const sketchFilePath = FileUri.fsPath(sketchUri); const sketchpath = dirname(sketchFilePath); @@ -104,15 +104,15 @@ export class CoreServiceImpl implements CoreService { try { await new Promise((resolve, reject) => { result.on('data', (resp: UploadResp) => { - this.outputService.append({ name: task, chunk: Buffer.from(resp.getOutStream_asU8()).toString() }); - this.outputService.append({ name: task, chunk: Buffer.from(resp.getErrStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(resp.getOutStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(resp.getErrStream_asU8()).toString() }); }); result.on('error', error => reject(error)); result.on('end', () => resolve()); }); - this.outputService.append({ name: 'upload', chunk: '\n--------------------------\n' + firstToLowerCase(task) + ' complete.\n' }); + this.outputService.append({ chunk: '\n--------------------------\n' + firstToLowerCase(task) + ' complete.\n' }); } catch (e) { - this.outputService.append({ name: 'upload', chunk: `${firstToUpperCase(task)} error: ${e}\n`, severity: 'error' }); + this.outputService.append({ chunk: `${firstToUpperCase(task)} error: ${e}\n`, severity: 'error' }); throw e; } } @@ -138,14 +138,14 @@ export class CoreServiceImpl implements CoreService { try { await new Promise((resolve, reject) => { result.on('data', (resp: BurnBootloaderResp) => { - this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() }); - this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(resp.getOutStream_asU8()).toString() }); + this.outputService.append({ chunk: Buffer.from(resp.getErrStream_asU8()).toString() }); }); result.on('error', error => reject(error)); result.on('end', () => resolve()); }); } catch (e) { - this.outputService.append({ name: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' }); + this.outputService.append({ chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' }); throw e; } } diff --git a/arduino-ide-extension/src/node/library-service-server-impl.ts b/arduino-ide-extension/src/node/library-service-server-impl.ts index 3142f9c93..2b6db3e67 100644 --- a/arduino-ide-extension/src/node/library-service-server-impl.ts +++ b/arduino-ide-extension/src/node/library-service-server-impl.ts @@ -177,7 +177,7 @@ export class LibraryServiceImpl implements LibraryService { resp.on('data', (r: LibraryInstallResp) => { const prog = r.getProgress(); if (prog) { - this.outputService.append({ name: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` }); + this.outputService.append({ chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` }); } }); await new Promise((resolve, reject) => { @@ -209,7 +209,7 @@ export class LibraryServiceImpl implements LibraryService { const resp = client.libraryUninstall(req); resp.on('data', (_: LibraryUninstallResp) => { if (!logged) { - this.outputService.append({ name: 'library', chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` }); + this.outputService.append({ chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` }); logged = true; } }); diff --git a/arduino-ide-extension/src/node/sketches-service-impl.ts b/arduino-ide-extension/src/node/sketches-service-impl.ts index 5cc23af92..6183ef4fd 100644 --- a/arduino-ide-extension/src/node/sketches-service-impl.ts +++ b/arduino-ide-extension/src/node/sketches-service-impl.ts @@ -18,7 +18,7 @@ import { LoadSketchReq, ArchiveSketchReq } from './cli-protocol/commands/command const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/; -const prefix = '.arduinoProIDE-unsaved'; +const prefix = '.arduinoIDE-unsaved'; @injectable() export class SketchesServiceImpl implements SketchesService { diff --git a/browser-app/package.json b/browser-app/package.json index 18d33ba94..abafa159b 100644 --- a/browser-app/package.json +++ b/browser-app/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "browser-app", - "version": "0.1.3", + "version": "2.0.0-beta.1", "license": "MIT", "dependencies": { "@theia/core": "next", @@ -18,7 +18,7 @@ "@theia/process": "next", "@theia/terminal": "next", "@theia/workspace": "next", - "arduino-ide-extension": "0.1.3" + "arduino-ide-extension": "2.0.0-beta.1" }, "devDependencies": { "@theia/cli": "next" @@ -31,9 +31,9 @@ "theia": { "frontend": { "config": { - "applicationName": "Arduino Pro IDE", + "applicationName": "Arduino IDE", "defaultTheme": "arduino-theme", - "status": "Alpha", + "status": "Beta", "preferences": { "editor.autoSave": "on", "editor.minimap.enabled": false, @@ -44,7 +44,7 @@ }, "backend": { "config": { - "configDirName": ".arduinoProIDE" + "configDirName": ".arduinoIDE" } }, "generator": { diff --git a/electron-app/package.json b/electron-app/package.json index 5570ed68c..c88bbbecc 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "electron-app", - "version": "0.1.3", + "version": "2.0.0-beta.1", "license": "MIT", "main": "src-gen/frontend/electron-main.js", "dependencies": { @@ -20,7 +20,7 @@ "@theia/process": "next", "@theia/terminal": "next", "@theia/workspace": "next", - "arduino-ide-extension": "0.1.3" + "arduino-ide-extension": "2.0.0-beta.1" }, "devDependencies": { "@theia/cli": "next" @@ -34,9 +34,9 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Arduino Pro IDE", + "applicationName": "Arduino IDE", "defaultTheme": "arduino-theme", - "status": "Alpha", + "status": "Beta", "preferences": { "editor.autoSave": "on", "editor.minimap.enabled": false, @@ -47,7 +47,7 @@ }, "backend": { "config": { - "configDirName": ".arduinoProIDE" + "configDirName": ".arduinoIDE" } }, "generator": { diff --git a/electron/README.md b/electron/README.md index b990b3f18..9eddc504b 100644 --- a/electron/README.md +++ b/electron/README.md @@ -1,13 +1,13 @@ ## Electron -All-in-one packager producing the `Arduino Pro IDE` Electron-based application. +All-in-one packager producing the `Arduino IDE` Electron-based application. ## Prerequisites The prerequisites are defined [here](https://github.com/theia-ide/theia/blob/master/doc/Developing.md#prerequisites). ## Build -To build the Arduino Pro IDE application you have to do the followings: +To build the Arduino IDE application you have to do the followings: ```bash yarn --cwd ./electron/packager/ && yarn --cwd ./electron/packager/ package ``` diff --git a/electron/build/patch/main.js b/electron/build/patch/main.js index b55784dfb..b15d72e40 100644 --- a/electron/build/patch/main.js +++ b/electron/build/patch/main.js @@ -1,7 +1,7 @@ // @ts-check const { setup, log } = require('node-log-rotate'); setup({ - appName: 'Arduino Pro IDE', + appName: 'Arduino IDE', maxSize: 10 * 1024 * 1024 }); for (const name of ['log', 'trace', 'info', 'warn', 'error']) { @@ -15,7 +15,7 @@ for (const name of ['log', 'trace', 'info', 'warn', 'error']) { const { BackendApplicationConfigProvider } = require('@theia/core/lib/node/backend-application-config-provider'); const main = require('@theia/core/lib/node/main'); BackendApplicationConfigProvider.set({ - "configDirName": ".arduinoProIDE", + "configDirName": ".arduinoIDE", "singleInstance": true }); diff --git a/electron/build/resources/icon.icns b/electron/build/resources/icon.icns index 121845f11..5fab6e0d1 100644 Binary files a/electron/build/resources/icon.icns and b/electron/build/resources/icon.icns differ diff --git a/electron/build/resources/icon.ico b/electron/build/resources/icon.ico index 05ea727c0..1db9b9f4e 100644 Binary files a/electron/build/resources/icon.ico and b/electron/build/resources/icon.ico differ diff --git a/electron/build/resources/icons/512x512.png b/electron/build/resources/icons/512x512.png index 2adb8d4bc..30911905e 100644 Binary files a/electron/build/resources/icons/512x512.png and b/electron/build/resources/icons/512x512.png differ diff --git a/electron/build/scripts/arduino-pro-ide-electron-main.js b/electron/build/scripts/arduino-ide-electron-main.js similarity index 100% rename from electron/build/scripts/arduino-pro-ide-electron-main.js rename to electron/build/scripts/arduino-ide-electron-main.js diff --git a/electron/build/template-package.json b/electron/build/template-package.json index c092aaac0..27cca42a6 100644 --- a/electron/build/template-package.json +++ b/electron/build/template-package.json @@ -1,5 +1,5 @@ { - "main": "scripts/arduino-pro-ide-electron-main.js", + "main": "scripts/arduino-ide-electron-main.js", "author": "Arduino SA", "resolutions": { "**/fs-extra": "^4.0.3" @@ -29,7 +29,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/arduino/arduino-pro-ide.git" + "url": "git+https://github.com/arduino/arduino-ide.git" }, "// Notes:": [ "The resolution for `fs-extra` was required due to this: https://spectrum.chat/theia/general/our-theia-electron-builder-app-no-longer-starts~f5cf09a0-6d88-448b-8818-24ad0ec2ee7c" @@ -48,7 +48,7 @@ } }, "build": { - "productName": "Arduino Pro IDE", + "productName": "Arduino IDE", "asar": false, "directories": { "buildResources": "resources" @@ -130,7 +130,7 @@ { "provider": "s3", "bucket": "arduino-downloads-prod-beagle", - "path": "arduino-pro-ide/nightly" + "path": "arduino-ide/nightly" } ] }, diff --git a/electron/packager/config.js b/electron/packager/config.js index 08a78eeb9..2efb0eaa4 100644 --- a/electron/packager/config.js +++ b/electron/packager/config.js @@ -18,7 +18,7 @@ function artifactName() { return getVersion(); } })(); - const name = 'arduino-pro-ide'; + const name = 'arduino-ide'; switch (platform) { case 'win32': { if (arch === 'x64') { @@ -99,8 +99,8 @@ function generateTemplate(buildDate) { // do `export PUBLISH=true yarn package` if you want to mimic CI build locally. // const electronPublish = release || (isCI && currentBranch() === 'master') || process.env.PUBLISH === 'true'; const version = getVersion(); - const productName = 'Arduino Pro IDE'; - const name = 'arduino-pro-ide'; + const productName = 'Arduino IDE'; + const name = 'arduino-ide'; let customizations = { name, description: productName, diff --git a/electron/packager/index.js b/electron/packager/index.js index 44cb25e92..8358b2634 100644 --- a/electron/packager/index.js +++ b/electron/packager/index.js @@ -276,15 +276,15 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()} const filesToCopy = []; switch (platform) { case 'linux': { - filesToCopy.push(...glob.sync('**/arduino-pro-ide*.{zip,AppImage}', { cwd }).map(p => join(cwd, p))); + filesToCopy.push(...glob.sync('**/arduino-ide*.{zip,AppImage}', { cwd }).map(p => join(cwd, p))); break; } case 'win32': { - filesToCopy.push(...glob.sync('**/arduino-pro-ide*.{exe,msi,zip}', { cwd }).map(p => join(cwd, p))); + filesToCopy.push(...glob.sync('**/arduino-ide*.{exe,msi,zip}', { cwd }).map(p => join(cwd, p))); break; } case 'darwin': { - filesToCopy.push(...glob.sync('**/arduino-pro-ide*.{dmg,zip}', { cwd }).map(p => join(cwd, p))); + filesToCopy.push(...glob.sync('**/arduino-ide*.{dmg,zip}', { cwd }).map(p => join(cwd, p))); break; } default: { diff --git a/electron/packager/package.json b/electron/packager/package.json index e337dcfd2..c987f619a 100644 --- a/electron/packager/package.json +++ b/electron/packager/package.json @@ -2,7 +2,7 @@ "private": true, "name": "packager", "version": "1.0.0", - "description": "Packager for the Arduino Pro IDE electron application", + "description": "Packager for the Arduino IDE electron application", "main": "index.js", "scripts": { "prepare": "yarn test", diff --git a/package.json b/package.json index 2d782ca25..703c27f60 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arduino-editor", - "version": "0.1.3", - "description": "Arduino Pro IDE", + "version": "2.0.0-beta.1", + "description": "Arduino IDE", "repository": "https://github.com/bcmi-labs/arduino-editor.git", "author": "Arduino SA", "license": "MIT", diff --git a/scripts/update-version.js b/scripts/update-version.js index 783617a6a..2cf9b74bf 100644 --- a/scripts/update-version.js +++ b/scripts/update-version.js @@ -19,7 +19,7 @@ if (!semver.valid(targetVersion)) { } if (!semver.gt(targetVersion, currentVersion)) { - console.error(`Target version '${targetVersion}' must be greater than the current version '${currentVersion}.'`); + console.error(`Target version '${targetVersion}' must be greater than the current version '${currentVersion}'.`); process.exit(1); } @@ -28,8 +28,7 @@ for (const toUpdate of [ path.join(repoRootPath, 'package.json'), path.join(repoRootPath, 'electron-app', 'package.json'), path.join(repoRootPath, 'browser-app', 'package.json'), - path.join(repoRootPath, 'arduino-ide-extension', 'package.json'), - path.join(repoRootPath, 'arduino-debugger-extension', 'package.json') // Currently unused. The debugger functionality comes from the `cortex.debug` VS Code extension. + path.join(repoRootPath, 'arduino-ide-extension', 'package.json') ]) { process.stdout.write(` Updating ${toUpdate}'...`); const pkg = require(toUpdate);