diff --git a/.vscode/tasks.json b/.vscode/tasks.json index eec0a9bb..9c365027 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -35,18 +35,7 @@ "panel": "new", "clear": false } - }, - { - "label": "Arduino Pro IDE - Watch Debugger Extension", - "type": "shell", - "command": "yarn --cwd ./arduino-debugger-extension watch", - "group": "build", - "presentation": { - "reveal": "always", - "panel": "new", - "clear": false - } - }, + } { "label": "Arduino Pro IDE - Watch Browser App", "type": "shell", @@ -74,7 +63,6 @@ "type": "shell", "dependsOn": [ "Arduino Pro IDE - Watch IDE Extension", - "Arduino Pro IDE - Watch Debugger Extension", "Arduino Pro IDE - Watch Browser App" ] }, @@ -83,7 +71,6 @@ "type": "shell", "dependsOn": [ "Arduino Pro IDE - Watch IDE Extension", - "Arduino Pro IDE - Watch Debugger Extension", "Arduino Pro IDE - Watch Electron App" ] } 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 f15386d7..2f5abb38 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -127,6 +127,7 @@ import { TabBarRenderer } from './theia/core/tab-bars'; import { EditorCommandContribution } from './theia/editor/editor-command'; import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator'; import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator'; +import { Debug } from './contributions/debug'; const ElementQueries = require('css-element-queries/src/ElementQueries'); @@ -327,6 +328,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, LibraryExamples); Contribution.configure(bind, IncludeLibrary); Contribution.configure(bind, About); + Contribution.configure(bind, Debug); bind(OutputServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, outputService) => { WebSocketConnectionProvider.createProxy(container, OutputServicePath, outputService); diff --git a/arduino-ide-extension/src/browser/contributions/debug.ts b/arduino-ide-extension/src/browser/contributions/debug.ts new file mode 100644 index 00000000..e6855d31 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/debug.ts @@ -0,0 +1,134 @@ +import { inject, injectable } from 'inversify'; +import { Event, Emitter } from '@theia/core/lib/common/event'; +import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin'; +import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; +import { NotificationCenter } from '../notification-center'; +import { Board, BoardsService, ExecutableService } from '../../common/protocol'; +import { BoardsServiceProvider } from '../boards/boards-service-provider'; +import { URI, Command, CommandRegistry, SketchContribution, TabBarToolbarRegistry } from './contribution'; + +@injectable() +export class Debug extends SketchContribution { + + @inject(HostedPluginSupport) + protected hostedPluginSupport: HostedPluginSupport; + + @inject(NotificationCenter) + protected readonly notificationCenter: NotificationCenter; + + @inject(ExecutableService) + protected readonly executableService: ExecutableService; + + @inject(BoardsService) + protected readonly boardService: BoardsService; + + @inject(BoardsServiceProvider) + protected readonly boardsServiceProvider: BoardsServiceProvider; + + /** + * If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled. + */ + protected _disabledMessages?: string = 'No board selected'; // Initial pessimism. + protected disabledMessageDidChangeEmitter = new Emitter(); + protected onDisabledMessageDidChange = this.disabledMessageDidChangeEmitter.event; + + protected get disabledMessage(): string | undefined { + return this._disabledMessages; + } + protected set disabledMessage(message: string | undefined) { + this._disabledMessages = message; + this.disabledMessageDidChangeEmitter.fire(this._disabledMessages); + } + + protected readonly debugToolbarItem = { + id: Debug.Commands.START_DEBUGGING.id, + command: Debug.Commands.START_DEBUGGING.id, + tooltip: `${this.disabledMessage ? `Debug - ${this.disabledMessage}` : 'Start Debugging'}`, + priority: 3, + onDidChange: this.onDisabledMessageDidChange as Event + }; + + onStart(): void { + this.onDisabledMessageDidChange(() => this.debugToolbarItem.tooltip = `${this.disabledMessage ? `Debug - ${this.disabledMessage}` : 'Start Debugging'}`); + const refreshState = async (board: Board | undefined = this.boardsServiceProvider.boardsConfig.selectedBoard) => { + if (!board) { + this.disabledMessage = 'No board selected'; + return; + } + const fqbn = board.fqbn; + if (!fqbn) { + this.disabledMessage = `Platform is not installed for '${board.name}'`; + return; + } + const details = await this.boardService.getBoardDetails({ fqbn }); + if (!details) { + this.disabledMessage = `Platform is not installed for '${board.name}'`; + return; + } + const { debuggingSupported } = details; + if (!debuggingSupported) { + this.disabledMessage = `Debugging is not supported by '${board.name}'`; + } else { + this.disabledMessage = undefined; + } + } + this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) => refreshState(selectedBoard)); + this.notificationCenter.onPlatformInstalled(() => refreshState()); + this.notificationCenter.onPlatformUninstalled(() => refreshState()); + refreshState(); + } + + registerCommands(registry: CommandRegistry): void { + registry.registerCommand(Debug.Commands.START_DEBUGGING, { + execute: () => this.startDebug(), + isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left', + isEnabled: () => !this.disabledMessage + }); + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem(this.debugToolbarItem); + } + + protected async startDebug(board: Board | undefined = this.boardsServiceProvider.boardsConfig.selectedBoard): Promise { + if (!board) { + return; + } + const { name, fqbn } = board; + if (!fqbn) { + return; + } + await this.hostedPluginSupport.didStart; + const [sketch, executables] = await Promise.all([ + this.sketchServiceClient.currentSketch(), + this.executableService.list() + ]); + if (!sketch) { + return; + } + const [cliPath, sketchPath] = await Promise.all([ + this.fileService.fsPath(new URI(executables.cliUri)), + this.fileService.fsPath(new URI(sketch.uri)) + ]) + const config = { + cliPath, + board: { + fqbn, + name + }, + sketchPath + }; + return this.commandService.executeCommand('arduino.debug.start', config); + } + +} + +export namespace Debug { + export namespace Commands { + export const START_DEBUGGING: Command = { + id: 'arduino-start-debug', + label: 'Start Debugging', + category: 'Arduino' + } + } +} diff --git a/arduino-debugger-extension/src/browser/style/debug-dark.svg b/arduino-ide-extension/src/browser/icons/debug-dark.svg similarity index 100% rename from arduino-debugger-extension/src/browser/style/debug-dark.svg rename to arduino-ide-extension/src/browser/icons/debug-dark.svg diff --git a/arduino-ide-extension/src/browser/style/main.css b/arduino-ide-extension/src/browser/style/main.css index fdf727a7..8bb9d125 100644 --- a/arduino-ide-extension/src/browser/style/main.css +++ b/arduino-ide-extension/src/browser/style/main.css @@ -64,6 +64,23 @@ mask-position: 28px -4px; } +.arduino-start-debug-icon { + -webkit-mask: url('../icons/debug-dark.svg') 50%; + mask: url('../icons/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; +} + #arduino-toolbar-container { display: flex; width: 100%; 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 dc4a89fc..257ef13d 100644 --- a/arduino-ide-extension/src/browser/theia/core/application-shell.ts +++ b/arduino-ide-extension/src/browser/theia/core/application-shell.ts @@ -68,6 +68,9 @@ export class ApplicationShell extends TheiaApplicationShell { } async saveAll(): Promise { + if (!this.canSaveAll()) { // This is a quick fix for not saving the editor when there are no dirty editors. + return; // https://github.com/bcmi-labs/arduino-editor/pull/172#issuecomment-741831888 + } if (this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE) { this.messageService.error('Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.'); return; // Theia does not reject on failed save: https://github.com/eclipse-theia/theia/pull/8803 diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts index 0d93d95d..76af168b 100644 --- a/arduino-ide-extension/src/common/protocol/boards-service.ts +++ b/arduino-ide-extension/src/common/protocol/boards-service.ts @@ -99,7 +99,7 @@ export interface BoardsService extends Installable, Searchable; getState(): Promise; - getBoardDetails(options: { fqbn: string }): Promise; + getBoardDetails(options: { fqbn: string }): Promise; getBoardPackage(options: { id: string }): Promise; getContainerBoardPackage(options: { fqbn: string }): Promise; // The CLI cannot do fuzzy search. This method provides all boards and we do the fuzzy search (with monaco) on the frontend. @@ -272,6 +272,7 @@ export interface BoardDetails { readonly requiredTools: Tool[]; readonly configOptions: ConfigOption[]; readonly programmers: Programmer[]; + readonly debuggingSupported: boolean; } export interface Tool { diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index 387f3fd5..a8fdb661 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -67,7 +67,7 @@ export class BoardsServiceImpl implements BoardsService { return coreClient; } - async getBoardDetails(options: { fqbn: string }): Promise { + async getBoardDetails(options: { fqbn: string }): Promise { const coreClient = await this.coreClient(); const { client, instance } = coreClient; const { fqbn } = options; @@ -77,7 +77,9 @@ export class BoardsServiceImpl implements BoardsService { const detailsResp = await new Promise((resolve, reject) => client.boardDetails(detailsReq, (err, resp) => { if (err) { // Required cores are not installed manually: https://github.com/arduino/arduino-cli/issues/954 - if (err.message.indexOf('missing platform release') !== -1 && err.message.indexOf('referenced by board') !== -1) { + if ((err.message.indexOf('missing platform release') !== -1 && err.message.indexOf('referenced by board') !== -1) + // Platform is not installed. + || err.message.indexOf('platform') !== -1 && err.message.indexOf('not installed') !== -1) { resolve(undefined); return; } @@ -88,14 +90,11 @@ export class BoardsServiceImpl implements BoardsService { })); if (!detailsResp) { - return { - fqbn, - configOptions: [], - programmers: [], - requiredTools: [] - }; + return undefined; } + const debuggingSupported = detailsResp.getDebuggingSupported(); + const requiredTools = detailsResp.getToolsdependenciesList().map(t => { name: t.getName(), packager: t.getPackager(), @@ -133,7 +132,8 @@ export class BoardsServiceImpl implements BoardsService { fqbn, requiredTools, configOptions, - programmers + programmers, + debuggingSupported }; } diff --git a/browser-app/package.json b/browser-app/package.json index 43e969b7..e204183b 100644 --- a/browser-app/package.json +++ b/browser-app/package.json @@ -18,8 +18,7 @@ "@theia/process": "next", "@theia/terminal": "next", "@theia/workspace": "next", - "arduino-ide-extension": "0.1.2", - "arduino-debugger-extension": "0.1.2" + "arduino-ide-extension": "0.1.2" }, "devDependencies": { "@theia/cli": "next" diff --git a/electron-app/package.json b/electron-app/package.json index 1675af5c..f5705d01 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -20,8 +20,7 @@ "@theia/process": "next", "@theia/terminal": "next", "@theia/workspace": "next", - "arduino-ide-extension": "0.1.2", - "arduino-debugger-extension": "0.1.2" + "arduino-ide-extension": "0.1.2" }, "devDependencies": { "@theia/cli": "next" diff --git a/electron/build/template-package.json b/electron/build/template-package.json index 04af71f7..4b6c7ebc 100644 --- a/electron/build/template-package.json +++ b/electron/build/template-package.json @@ -137,6 +137,7 @@ "theiaPluginsDir": "plugins", "theiaPlugins": { "vscode-builtin-cpp": "http://open-vsx.org/api/vscode/cpp/1.44.2/file/vscode.cpp-1.44.2.vsix", - "vscode-arduino-language-server": "https://downloads.arduino.cc/vscode-arduino-language-server/nightly/vscode-arduino-language-server-0.0.1.vsix" + "vscode-arduino-language-server": "https://downloads.arduino.cc/vscode-arduino-language-server/nightly/vscode-arduino-language-server-0.0.1.vsix", + "cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.7/file/marus25.cortex-debug-0.3.7.vsix" } } diff --git a/electron/packager/index.js b/electron/packager/index.js index 8deba70b..b52d0b4e 100644 --- a/electron/packager/index.js +++ b/electron/packager/index.js @@ -44,8 +44,7 @@ shell.exec(`git -C ${path('..', 'build')} clean -ffxdq`, { async: false }); const extensions = [ - 'arduino-ide-extension', - 'arduino-debugger-extension', + 'arduino-ide-extension' ]; const allDependencies = [ ...extensions, diff --git a/package.json b/package.json index 7b2fc321..fb100a80 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,13 @@ }, "workspaces": [ "arduino-ide-extension", - "arduino-debugger-extension", "electron-app", "browser-app" ], "theiaPluginsDir": "plugins", "theiaPlugins": { "vscode-builtin-cpp": "http://open-vsx.org/api/vscode/cpp/1.44.2/file/vscode.cpp-1.44.2.vsix", - "vscode-arduino-language-server": "https://downloads.arduino.cc/vscode-arduino-language-server/nightly/vscode-arduino-language-server-0.0.1.vsix" + "vscode-arduino-language-server": "https://downloads.arduino.cc/vscode-arduino-language-server/nightly/vscode-arduino-language-server-0.0.1.vsix", + "cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.7/file/marus25.cortex-debug-0.3.7.vsix" } } diff --git a/yarn.lock b/yarn.lock index 40a26ee5..73e10cdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5200,15 +5200,6 @@ caw@^2.0.1: tunnel-agent "^0.6.0" url-to-options "^1.0.1" -cdt-gdb-adapter@^0.0.14: - version "0.0.14" - resolved "https://registry.yarnpkg.com/cdt-gdb-adapter/-/cdt-gdb-adapter-0.0.14.tgz#0b4cc8650699572003e52a9f8265e9c65055339c" - integrity sha512-SDRVECyEkgePj0AJNVp8HUOw/ObXSlakQNxMasC1wlCn6eE8HcLAdYqdIgwRtnkp2Ct96/U5mXHFHWQDan6ckw== - dependencies: - node-addon-api "^1.6.2" - vscode-debugadapter "^1.37.1" - vscode-debugprotocol "^1.37.0" - chai-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/chai-string/-/chai-string-1.5.0.tgz#0bdb2d8a5f1dbe90bc78ec493c1c1c180dd4d3d2" @@ -10620,7 +10611,7 @@ mkdirp@*, mkdirp@^1.0.0, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@0.5.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@0.5.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -10856,11 +10847,6 @@ node-abi@^2.11.0, node-abi@^2.7.0: dependencies: semver "^5.4.1" -node-addon-api@^1.6.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" - integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== - node-dir@0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d" @@ -15058,15 +15044,7 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vscode-debugadapter@^1.26.0, vscode-debugadapter@^1.37.1: - version "1.42.1" - resolved "https://registry.yarnpkg.com/vscode-debugadapter/-/vscode-debugadapter-1.42.1.tgz#8fe1daa23548dbe4034a065c362d16a4bab18dc2" - integrity sha512-bICDB8mxReU2kGL13ftjNqeInYtVN3zpRdZKjRZHCpXC/mofrdaj9KRlJsALwcUNivMA9S/LwTZ2n+ros3X0jg== - dependencies: - mkdirp "^0.5.5" - vscode-debugprotocol "1.42.0" - -vscode-debugprotocol@1.42.0, vscode-debugprotocol@^1.26.0, vscode-debugprotocol@^1.32.0, vscode-debugprotocol@^1.37.0: +vscode-debugprotocol@^1.32.0: version "1.42.0" resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.42.0.tgz#86ad5d95c52a8fd255bc40476414b3a417160f84" integrity sha512-nVsfVCat9FZlOso5SYB1LQQiFGifTyOALpkpJdudDlRXGTpI3mSFiDYXWaoFm7UcfqTOzn1SC7Hqw4d89btT0w==