From d556ee95c0f80d6ffe71aa122be2214919dc996e Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Wed, 3 Aug 2022 09:01:18 +0200 Subject: [PATCH 01/14] Use FQBN instead of `Board` for the monitor ID. Closes #1278 Signed-off-by: Akos Kitta --- .../node/arduino-firmware-uploader-impl.ts | 4 +- .../src/node/core-service-impl.ts | 14 +++---- .../src/node/monitor-manager.ts | 39 ++++++++++--------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/arduino-ide-extension/src/node/arduino-firmware-uploader-impl.ts b/arduino-ide-extension/src/node/arduino-firmware-uploader-impl.ts index d693ce758..a14f406d1 100644 --- a/arduino-ide-extension/src/node/arduino-firmware-uploader-impl.ts +++ b/arduino-ide-extension/src/node/arduino-firmware-uploader-impl.ts @@ -76,7 +76,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader { fqbn: firmware.board_fqbn, }; try { - await this.monitorManager.notifyUploadStarted(board, port); + await this.monitorManager.notifyUploadStarted(board.fqbn, port); output = await this.runCommand([ 'firmware', 'flash', @@ -90,7 +90,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader { } catch (e) { throw e; } finally { - await this.monitorManager.notifyUploadFinished(board, port); + await this.monitorManager.notifyUploadFinished(board.fqbn, port); return output; } } diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 7072a34dc..a7dde4f2b 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -23,7 +23,7 @@ import { UploadUsingProgrammerResponse, } from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb'; import { ResponseService } from '../common/protocol/response-service'; -import { Board, OutputMessage, Port, Status } from '../common/protocol'; +import { OutputMessage, Port, Status } from '../common/protocol'; import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb'; import { Port as GrpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb'; import { ApplicationError, CommandService, Disposable, nls } from '@theia/core'; @@ -376,23 +376,23 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { } private async notifyUploadWillStart({ - board, + fqbn, port, }: { - board?: Board | undefined; + fqbn?: string | undefined; port?: Port | undefined; }): Promise { - return this.monitorManager.notifyUploadStarted(board, port); + return this.monitorManager.notifyUploadStarted(fqbn, port); } private async notifyUploadDidFinish({ - board, + fqbn, port, }: { - board?: Board | undefined; + fqbn?: string | undefined; port?: Port | undefined; }): Promise { - return this.monitorManager.notifyUploadFinished(board, port); + return this.monitorManager.notifyUploadFinished(fqbn, port); } private mergeSourceOverrides( diff --git a/arduino-ide-extension/src/node/monitor-manager.ts b/arduino-ide-extension/src/node/monitor-manager.ts index a671a14c4..38360a39f 100644 --- a/arduino-ide-extension/src/node/monitor-manager.ts +++ b/arduino-ide-extension/src/node/monitor-manager.ts @@ -58,7 +58,7 @@ export class MonitorManager extends CoreClientAware { * combination specified, false in all other cases. */ isStarted(board: Board, port: Port): boolean { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); const monitor = this.monitorServices.get(monitorID); if (monitor) { return monitor.isStarted(); @@ -106,7 +106,7 @@ export class MonitorManager extends CoreClientAware { port: Port, connectToClient: (status: Status) => void ): Promise { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); let monitor = this.monitorServices.get(monitorID); if (!monitor) { @@ -138,7 +138,7 @@ export class MonitorManager extends CoreClientAware { * @param port port monitored */ async stopMonitor(board: Board, port: Port): Promise { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); const monitor = this.monitorServices.get(monitorID); if (!monitor) { // There's no monitor to stop, bail @@ -155,7 +155,7 @@ export class MonitorManager extends CoreClientAware { * @returns port of the MonitorService's WebSocket */ getWebsocketAddressPort(board: Board, port: Port): number { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); const monitor = this.monitorServices.get(monitorID); if (!monitor) { return -1; @@ -168,17 +168,17 @@ export class MonitorManager extends CoreClientAware { * that an upload process started on that exact board/port combination. * This must be done so that we can stop the monitor for the time being * until the upload process finished. - * @param board board connected to port + * @param fqbn the FQBN of the board connected to port * @param port port to monitor */ - async notifyUploadStarted(board?: Board, port?: Port): Promise { - if (!board || !port) { + async notifyUploadStarted(fqbn?: string, port?: Port): Promise { + if (!fqbn || !port) { // We have no way of knowing which monitor // to retrieve if we don't have this information. return; } - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(fqbn, port); this.addToMonitorIDsByUploadState('uploadInProgress', monitorID); const monitor = this.monitorServices.get(monitorID); @@ -194,19 +194,22 @@ export class MonitorManager extends CoreClientAware { /** * Notifies the monitor service of that board/port combination * that an upload process started on that exact board/port combination. - * @param board board connected to port + * @param fqbn the FQBN of the board connected to port * @param port port to monitor * @returns a Status object to know if the process has been * started or if there have been errors. */ - async notifyUploadFinished(board?: Board, port?: Port): Promise { + async notifyUploadFinished( + fqbn?: string | undefined, + port?: Port + ): Promise { let status: Status = Status.NOT_CONNECTED; let portDidChangeOnUpload = false; // We have no way of knowing which monitor // to retrieve if we don't have this information. - if (board && port) { - const monitorID = this.monitorID(board, port); + if (fqbn && port) { + const monitorID = this.monitorID(fqbn, port); this.removeFromMonitorIDsByUploadState('uploadInProgress', monitorID); const monitor = this.monitorServices.get(monitorID); @@ -277,7 +280,7 @@ export class MonitorManager extends CoreClientAware { port: Port, settings: PluggableMonitorSettings ) { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); let monitor = this.monitorServices.get(monitorID); if (!monitor) { monitor = this.createMonitor(board, port); @@ -296,7 +299,7 @@ export class MonitorManager extends CoreClientAware { board: Board, port: Port ): Promise { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); const monitor = this.monitorServices.get(monitorID); if (!monitor) { return {}; @@ -312,7 +315,7 @@ export class MonitorManager extends CoreClientAware { * @returns a new instance of MonitorService ready to use. */ private createMonitor(board: Board, port: Port): MonitorService { - const monitorID = this.monitorID(board, port); + const monitorID = this.monitorID(board.fqbn, port); const monitor = this.monitorServiceFactory({ board, port, @@ -341,12 +344,12 @@ export class MonitorManager extends CoreClientAware { /** * Utility function to create a unique ID for a monitor service. - * @param board + * @param fqbn * @param port * @returns a unique monitor ID */ - private monitorID(board: Board, port: Port): MonitorID { - const splitFqbn = board?.fqbn?.split(':') || []; + private monitorID(fqbn: string | undefined, port: Port): MonitorID { + const splitFqbn = fqbn?.split(':') || []; const shortenedFqbn = splitFqbn.slice(0, 3).join(':') || ''; return `${shortenedFqbn}-${port.address}-${port.protocol}`; } From 879aedeaa35144eced049ddeea433d26c0ad8235 Mon Sep 17 00:00:00 2001 From: InstantMuffin Date: Wed, 3 Aug 2022 16:43:01 +0200 Subject: [PATCH 02/14] Update BUILDING.md (#1281) * Update BUILDING.md Added "Notes for Linux contributors" based on my own building experience * Update BUILDING.md Removing the linux specific section and instead updating the Theia IDE prerequisites link to point to the mentioned file directly. --- BUILDING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index ed13b2e93..15499bfdd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -41,7 +41,7 @@ The _frontend_ is running as an Electron renderer process and can invoke service 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 IDE locally. -Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions. +Please refer to the [Theia IDE prerequisites](https://github.com/eclipse-theia/theia/blob/master/doc/Developing.md#prerequisites) documentation for the setup instructions. > **Note**: Node.js 14 must be used instead of the version 12 recommended at the link above. Once you have all the tools installed, you can build the editor following these steps @@ -89,7 +89,6 @@ This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide git push origin 1.2.3 ``` - ## Notes for macOS contributors Beginning in macOS 10.14.5, the software [must be notarized to run](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution). The signing and notarization processes for the Arduino IDE are managed by our Continuous Integration (CI) workflows, implemented with GitHub Actions. On every push and pull request, the Arduino IDE is built and saved to a workflow artifact. These artifacts can be used by contributors and beta testers who don't want to set up a build system locally. For security reasons, signing and notarization are disabled for workflow runs for pull requests from forks of this repository. This means that macOS will block you from running those artifacts. From bf193b1cac35a7dad1cfb6029541d14be1adfeb7 Mon Sep 17 00:00:00 2001 From: Akos Kitta <1405703+kittaakos@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:28:28 +0200 Subject: [PATCH 03/14] Pinned `2dd8976` CLI in the IDE2. (#1280) Signed-off-by: Akos Kitta --- arduino-ide-extension/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 713c7868c..1ffc4cd55 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -156,7 +156,11 @@ ], "arduino": { "cli": { - "version": "0.25.1" + "version": { + "owner": "arduino", + "repo": "arduino-cli", + "commitish": "2dd8976" + } }, "fwuploader": { "version": "2.2.0" From 36ac47b975b6c3e54cb115f2c71b519924744613 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Wed, 3 Aug 2022 16:17:37 +0200 Subject: [PATCH 04/14] Can check if the current window is the first one. Closes #1070 Signed-off-by: Akos Kitta --- arduino-ide-extension/package.json | 4 ++ .../contributions/first-startup-installer.ts | 4 +- .../theia/core/browser-window-module.ts | 10 +++++ .../theia/core/default-window-service.ts | 17 ++++++++ .../browser/theia/core/window-service-ext.ts | 7 +++ .../theia/core/electron-menu-module.ts | 37 +++++----------- .../theia/core/electron-window-module.ts | 32 ++++++++++++++ .../core}/electron-window-service.ts | 35 ++++++++++++--- .../electron-main-window-service-ext.ts | 7 +++ .../arduino-electron-main-module.ts | 43 ++++++++----------- .../electron-main-window-service-ext-impl.ts | 15 +++++++ .../theia/electron-main-application.ts | 31 ++++++++----- .../src/node/arduino-ide-backend-module.ts | 18 ++++++++ 13 files changed, 191 insertions(+), 69 deletions(-) create mode 100644 arduino-ide-extension/src/browser/theia/core/browser-window-module.ts create mode 100644 arduino-ide-extension/src/browser/theia/core/default-window-service.ts create mode 100644 arduino-ide-extension/src/browser/theia/core/window-service-ext.ts create mode 100644 arduino-ide-extension/src/electron-browser/theia/core/electron-window-module.ts rename arduino-ide-extension/src/electron-browser/{ => theia/core}/electron-window-service.ts (61%) create mode 100644 arduino-ide-extension/src/electron-common/electron-main-window-service-ext.ts create mode 100644 arduino-ide-extension/src/electron-main/electron-main-window-service-ext-impl.ts diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 1ffc4cd55..e28a03c7a 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -150,6 +150,10 @@ "frontend": "lib/browser/theia/core/browser-menu-module", "frontendElectron": "lib/electron-browser/theia/core/electron-menu-module" }, + { + "frontend": "lib/browser/theia/core/browser-window-module", + "frontendElectron": "lib/electron-browser/theia/core/electron-window-module" + }, { "electronMain": "lib/electron-main/arduino-electron-main-module" } diff --git a/arduino-ide-extension/src/browser/contributions/first-startup-installer.ts b/arduino-ide-extension/src/browser/contributions/first-startup-installer.ts index 5010c5fc9..8f469ee7d 100644 --- a/arduino-ide-extension/src/browser/contributions/first-startup-installer.ts +++ b/arduino-ide-extension/src/browser/contributions/first-startup-installer.ts @@ -43,7 +43,7 @@ export class FirstStartupInstaller extends Contribution { // If arduino:avr installation fails because it's already installed we don't want to retry on next start-up console.error(e); } else { - // But if there is any other error (e.g.: no interntet cconnection), we want to retry next time + // But if there is any other error (e.g.: no Internet connection), we want to retry next time avrPackageError = e; } } @@ -64,7 +64,7 @@ export class FirstStartupInstaller extends Contribution { // If Arduino_BuiltIn installation fails because it's already installed we don't want to retry on next start-up console.log('error installing core', e); } else { - // But if there is any other error (e.g.: no interntet cconnection), we want to retry next time + // But if there is any other error (e.g.: no Internet connection), we want to retry next time builtInLibraryError = e; } } diff --git a/arduino-ide-extension/src/browser/theia/core/browser-window-module.ts b/arduino-ide-extension/src/browser/theia/core/browser-window-module.ts new file mode 100644 index 000000000..ac0862b75 --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/core/browser-window-module.ts @@ -0,0 +1,10 @@ +import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { DefaultWindowService } from './default-window-service'; +import { WindowServiceExt } from './window-service-ext'; + +export default new ContainerModule((bind, unbind, isBound, rebind) => { + bind(DefaultWindowService).toSelf().inSingletonScope(); + rebind(TheiaDefaultWindowService).toService(DefaultWindowService); + bind(WindowServiceExt).toService(DefaultWindowService); +}); diff --git a/arduino-ide-extension/src/browser/theia/core/default-window-service.ts b/arduino-ide-extension/src/browser/theia/core/default-window-service.ts new file mode 100644 index 000000000..145691727 --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/core/default-window-service.ts @@ -0,0 +1,17 @@ +import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service'; +import { injectable } from '@theia/core/shared/inversify'; +import { WindowServiceExt } from './window-service-ext'; + +@injectable() +export class DefaultWindowService + extends TheiaDefaultWindowService + implements WindowServiceExt +{ + /** + * The default implementation always resolves to `true`. + * IDE2 does not use it. It's currently an electron-only app. + */ + async isFirstWindow(): Promise { + return true; + } +} diff --git a/arduino-ide-extension/src/browser/theia/core/window-service-ext.ts b/arduino-ide-extension/src/browser/theia/core/window-service-ext.ts new file mode 100644 index 000000000..f22e55cc4 --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/core/window-service-ext.ts @@ -0,0 +1,7 @@ +export const WindowServiceExt = Symbol('WindowServiceExt'); +export interface WindowServiceExt { + /** + * Returns with a promise that resolves to `true` if the current window is the first window. + */ + isFirstWindow(): Promise; +} diff --git a/arduino-ide-extension/src/electron-browser/theia/core/electron-menu-module.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-menu-module.ts index bed0179ee..101e44b56 100644 --- a/arduino-ide-extension/src/electron-browser/theia/core/electron-menu-module.ts +++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-menu-module.ts @@ -1,14 +1,7 @@ import { ContainerModule } from '@theia/core/shared/inversify'; -import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { ElectronMainMenuFactory as TheiaElectronMainMenuFactory } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory'; import { ElectronMenuContribution as TheiaElectronMenuContribution } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution'; -import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider'; -import { - SplashService, - splashServicePath, -} from '../../../electron-common/splash-service'; import { MainMenuManager } from '../../../common/main-menu-manager'; -import { ElectronWindowService } from '../../electron-window-service'; import { ElectronMainMenuFactory } from './electron-main-menu-factory'; import { ElectronMenuContribution } from './electron-menu-contribution'; import { nls } from '@theia/core/lib/common/nls'; @@ -16,23 +9,25 @@ import { nls } from '@theia/core/lib/common/nls'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as dialogs from '@theia/core/lib/browser/dialogs'; - Object.assign(dialogs, { confirmExit: async () => { const messageBoxResult = await remote.dialog.showMessageBox( remote.getCurrentWindow(), { - message: nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'), - title: nls.localize('theia/core/quitTitle', 'Are you sure you want to quit?'), + message: nls.localize( + 'theia/core/quitMessage', + 'Any unsaved changes will not be saved.' + ), + title: nls.localize( + 'theia/core/quitTitle', + 'Are you sure you want to quit?' + ), type: 'question', - buttons: [ - dialogs.Dialog.CANCEL, - dialogs.Dialog.YES, - ], + buttons: [dialogs.Dialog.CANCEL, dialogs.Dialog.YES], } - ) + ); return messageBoxResult.response === 1; - } + }, }); export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -41,14 +36,4 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution); bind(ElectronMainMenuFactory).toSelf().inSingletonScope(); rebind(TheiaElectronMainMenuFactory).toService(ElectronMainMenuFactory); - bind(ElectronWindowService).toSelf().inSingletonScope(); - rebind(WindowService).toService(ElectronWindowService); - bind(SplashService) - .toDynamicValue((context) => - ElectronIpcConnectionProvider.createProxy( - context.container, - splashServicePath - ) - ) - .inSingletonScope(); }); diff --git a/arduino-ide-extension/src/electron-browser/theia/core/electron-window-module.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-window-module.ts new file mode 100644 index 000000000..9328572b1 --- /dev/null +++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-window-module.ts @@ -0,0 +1,32 @@ +import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext'; +import { + ElectronMainWindowServiceExt, + electronMainWindowServiceExtPath, +} from '../../../electron-common/electron-main-window-service-ext'; +import { + SplashService, + splashServicePath, +} from '../../../electron-common/splash-service'; +import { ElectronWindowService } from './electron-window-service'; + +export default new ContainerModule((bind, unbind, isBound, rebind) => { + bind(ElectronWindowService).toSelf().inSingletonScope(); + rebind(WindowService).toService(ElectronWindowService); + bind(WindowServiceExt).toService(ElectronWindowService); + bind(ElectronMainWindowServiceExt) + .toDynamicValue(({ container }) => + ElectronIpcConnectionProvider.createProxy( + container, + electronMainWindowServiceExtPath + ) + ) + .inSingletonScope(); + bind(SplashService) + .toDynamicValue(({ container }) => + ElectronIpcConnectionProvider.createProxy(container, splashServicePath) + ) + .inSingletonScope(); +}); diff --git a/arduino-ide-extension/src/electron-browser/electron-window-service.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-window-service.ts similarity index 61% rename from arduino-ide-extension/src/electron-browser/electron-window-service.ts rename to arduino-ide-extension/src/electron-browser/theia/core/electron-window-service.ts index 1407ac425..2f571b419 100644 --- a/arduino-ide-extension/src/electron-browser/electron-window-service.ts +++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-window-service.ts @@ -1,4 +1,8 @@ -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { + inject, + injectable, + postConstruct, +} from '@theia/core/shared/inversify'; import * as remote from '@theia/core/electron-shared/@electron/remote'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { @@ -6,19 +10,27 @@ import { ConnectionStatusService, } from '@theia/core/lib/browser/connection-status-service'; import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service'; -import { SplashService } from '../electron-common/splash-service'; +import { SplashService } from '../../../electron-common/splash-service'; import { nls } from '@theia/core/lib/common'; +import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext'; +import { ElectronMainWindowServiceExt } from '../../../electron-common/electron-main-window-service-ext'; @injectable() -export class ElectronWindowService extends TheiaElectronWindowService { +export class ElectronWindowService + extends TheiaElectronWindowService + implements WindowServiceExt +{ @inject(ConnectionStatusService) - protected readonly connectionStatusService: ConnectionStatusService; + private readonly connectionStatusService: ConnectionStatusService; @inject(SplashService) - protected readonly splashService: SplashService; + private readonly splashService: SplashService; @inject(FrontendApplicationStateService) - protected readonly appStateService: FrontendApplicationStateService; + private readonly appStateService: FrontendApplicationStateService; + + @inject(ElectronMainWindowServiceExt) + private readonly mainWindowServiceExt: ElectronMainWindowServiceExt; @postConstruct() protected override init(): void { @@ -55,4 +67,15 @@ export class ElectronWindowService extends TheiaElectronWindowService { }); return response === 0; // 'Yes', close the window. } + + private _firstWindow: boolean | undefined; + async isFirstWindow(): Promise { + if (this._firstWindow === undefined) { + const windowId = remote.getCurrentWindow().id; // This is expensive and synchronous so we check it once per FE. + this._firstWindow = await this.mainWindowServiceExt.isFirstWindow( + windowId + ); + } + return this._firstWindow; + } } diff --git a/arduino-ide-extension/src/electron-common/electron-main-window-service-ext.ts b/arduino-ide-extension/src/electron-common/electron-main-window-service-ext.ts new file mode 100644 index 000000000..c0e52d353 --- /dev/null +++ b/arduino-ide-extension/src/electron-common/electron-main-window-service-ext.ts @@ -0,0 +1,7 @@ +export const electronMainWindowServiceExtPath = '/services/electron-window-ext'; +export const ElectronMainWindowServiceExt = Symbol( + 'ElectronMainWindowServiceExt' +); +export interface ElectronMainWindowServiceExt { + isFirstWindow(windowId: number): Promise; +} 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 7da0c7314..0146fd02a 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,31 +1,31 @@ -import { ContainerModule } from '@theia/core/shared/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 { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler'; import { ElectronMainApplication as TheiaElectronMainApplication, ElectronMainApplicationContribution, } from '@theia/core/lib/electron-main/electron-main-application'; +import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { + IDEUpdater, + IDEUpdaterClient, + IDEUpdaterPath, +} from '../common/protocol/ide-updater'; +import { + ElectronMainWindowServiceExt, + electronMainWindowServiceExtPath, +} from '../electron-common/electron-main-window-service-ext'; import { SplashService, splashServicePath, } from '../electron-common/splash-service'; +import { ElectronMainWindowServiceExtImpl } from './electron-main-window-service-ext-impl'; +import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl'; import { SplashServiceImpl } from './splash/splash-service-impl'; import { ElectronMainApplication } from './theia/electron-main-application'; import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service'; -import { - IDEUpdater, - IDEUpdaterClient, - IDEUpdaterPath, -} from '../common/protocol/ide-updater'; -import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl'; import { TheiaElectronWindow } from './theia/theia-electron-window'; -import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window'; -import { SurveyNotificationServiceImpl } from '../node/survey-service-impl'; -import { - SurveyNotificationService, - SurveyNotificationServicePath, -} from '../common/protocol/survey-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ElectronMainApplication).toSelf().inSingletonScope(); @@ -67,19 +67,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(TheiaElectronWindow).toSelf(); rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow); - // Survey notification bindings - bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope(); - bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl); - bind(ElectronMainApplicationContribution).toService( - SurveyNotificationService - ); + bind(ElectronMainWindowServiceExt) + .to(ElectronMainWindowServiceExtImpl) + .inSingletonScope(); bind(ElectronConnectionHandler) .toDynamicValue( (context) => - new JsonRpcConnectionHandler(SurveyNotificationServicePath, () => - context.container.get( - SurveyNotificationService - ) + new JsonRpcConnectionHandler(electronMainWindowServiceExtPath, () => + context.container.get(ElectronMainWindowServiceExt) ) ) .inSingletonScope(); diff --git a/arduino-ide-extension/src/electron-main/electron-main-window-service-ext-impl.ts b/arduino-ide-extension/src/electron-main/electron-main-window-service-ext-impl.ts new file mode 100644 index 000000000..a0c872786 --- /dev/null +++ b/arduino-ide-extension/src/electron-main/electron-main-window-service-ext-impl.ts @@ -0,0 +1,15 @@ +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ElectronMainWindowServiceExt } from '../electron-common/electron-main-window-service-ext'; +import { ElectronMainApplication } from './theia/electron-main-application'; + +@injectable() +export class ElectronMainWindowServiceExtImpl + implements ElectronMainWindowServiceExt +{ + @inject(ElectronMainApplication) + private readonly app: ElectronMainApplication; + + async isFirstWindow(windowId: number): Promise { + return this.app.firstWindowId === windowId; + } +} 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 4a3a3ade2..e5159a149 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 @@ -66,11 +66,12 @@ const APP_STARTED_WITH_CONTENT_TRACE = @injectable() export class ElectronMainApplication extends TheiaElectronMainApplication { - protected startup = false; - protected openFilePromise = new Deferred(); + private startup = false; + private _firstWindowId: number | undefined; + private openFilePromise = new Deferred(); @inject(SplashServiceImpl) - protected readonly splashService: SplashServiceImpl; + private readonly splashService: SplashServiceImpl; override async start(config: FrontendApplicationConfig): Promise { // Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit") @@ -142,7 +143,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { })(); } - attachFileAssociations() { + private attachFileAssociations(): void { // OSX: register open-file event if (os.isOSX) { app.on('open-file', async (event, uri) => { @@ -158,7 +159,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { } } - protected async isValidSketchPath(uri: string): Promise { + private async isValidSketchPath(uri: string): Promise { return typeof uri === 'string' && (await fs.pathExists(uri)); } @@ -201,7 +202,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { } } - protected async launchFromArgs( + private async launchFromArgs( params: ElectronMainExecutionParams ): Promise { // Copy to prevent manipulation of original array @@ -223,7 +224,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { return false; } - protected async openSketch( + private async openSketch( workspace: WorkspaceOptions | string ): Promise { const options = await this.getLastWindowOptions(); @@ -257,7 +258,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { } protected override getTitleBarStyle( - config: FrontendApplicationConfig + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _config: FrontendApplicationConfig ): 'native' | 'custom' { return 'native'; } @@ -354,6 +356,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { electronWindow.webContents.openDevTools(); } this.attachListenersToWindow(electronWindow); + if (this._firstWindowId === undefined) { + this._firstWindowId = electronWindow.id; + } return electronWindow; } @@ -389,7 +394,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { }); event.newGuest = new BrowserWindow(options); event.newGuest.setMenu(null); - event.newGuest?.on('closed', (e: any) => { + event.newGuest?.on('closed', () => { electronWindow?.webContents.send('CLOSE_CHILD_WINDOW'); }); event.newGuest?.loadURL(url); @@ -462,9 +467,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { } } - protected closedWorkspaces: WorkspaceOptions[] = []; + private closedWorkspaces: WorkspaceOptions[] = []; - protected attachClosedWorkspace(window: BrowserWindow): void { + private attachClosedWorkspace(window: BrowserWindow): void { // Since the `before-quit` event is only fired when closing the *last* window // We need to keep track of recently closed windows/workspaces manually window.on('close', () => { @@ -522,4 +527,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { get browserWindows(): BrowserWindow[] { return Array.from(this.windows.values()).map(({ window }) => window); } + + get firstWindowId(): number | undefined { + return this._firstWindowId; + } } diff --git a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts index 6338b5320..b21f5d301 100644 --- a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts +++ b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts @@ -104,6 +104,11 @@ import { ClangFormatter } from './clang-formatter'; import { FormatterPath } from '../common/protocol/formatter'; import { LocalizationBackendContribution } from './i18n/localization-backend-contribution'; import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution'; +import { SurveyNotificationServiceImpl } from './survey-service-impl'; +import { + SurveyNotificationService, + SurveyNotificationServicePath, +} from '../common/protocol/survey-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(BackendApplication).toSelf().inSingletonScope(); @@ -401,4 +406,17 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { rebind(TheiaLocalizationBackendContribution).toService( LocalizationBackendContribution ); + + // Survey notification bindings + // It's currently unused. https://github.com/arduino/arduino-ide/pull/1150 + bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope(); + bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl); + bind(ConnectionHandler) + .toDynamicValue( + ({ container }) => + new JsonRpcConnectionHandler(SurveyNotificationServicePath, () => + container.get(SurveyNotificationService) + ) + ) + .inSingletonScope(); }); From 0b33b517008dc2b5e27e54aafc588200fbac9459 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 4 Aug 2022 14:03:41 +0200 Subject: [PATCH 05/14] Set `XDG_CONFIG_HOME` env on Linux when not set. Otherwise, `node-log-rotate` creates a folder with `undefined` name. Closes #394. Signed-off-by: Akos Kitta --- electron/build/patch/backend/main.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/electron/build/patch/backend/main.js b/electron/build/patch/backend/main.js index b15d72e40..cb138d3c4 100644 --- a/electron/build/patch/backend/main.js +++ b/electron/build/patch/backend/main.js @@ -1,4 +1,18 @@ // @ts-check + +// Patch for on Linux when `XDG_CONFIG_HOME` is not available, `node-log-rotate` creates the folder with `undefined` name. +// See https://github.com/lemon-sour/node-log-rotate/issues/23 and https://github.com/arduino/arduino-ide/issues/394. +// If the IDE2 is running on Linux, and the `XDG_CONFIG_HOME` variable is not available, set it to avoid the `undefined` folder. +// From the specs: https://specifications.freedesktop.org/basedir-spec/latest/ar01s03.html +// "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used." +const os = require('os'); +if (os.platform() === 'linux' && !process.env['XDG_CONFIG_HOME']) { + const { join } = require('path'); + const home = process.env['HOME']; + const xdgConfigHome = home ? join(home, '.config') : join(os.homedir(), '.config'); + process.env['XDG_CONFIG_HOME'] = xdgConfigHome; +} + const { setup, log } = require('node-log-rotate'); setup({ appName: 'Arduino IDE', From ce273adf777a8f1a0230ab2bba8cc013f2347c36 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 1 Aug 2022 14:59:36 -0700 Subject: [PATCH 06/14] Correctly escape escaped content in formatter configuration The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a command line argument. The quotes in the JSON syntax are escaped in order to make them compatible with this usage. Previously, consideration was not given to escaping of the content. For example, with the previous escaping code, this content: `\"` would be converted to `\\"`, whereas the correct escaping would look like `\\\"`. That did not result in problems only because the configuration didn't contain escaped content. This good fortune will not persist through updates to the configuration so the command must be properly processed. The content of the configuration will now be escaped in addition to the quotes of the JSON data format. --- arduino-ide-extension/src/node/clang-formatter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index b34b034ac..d7bdeb9e2 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -123,7 +123,10 @@ function toClangOptions( // See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { - return JSON.stringify(styleJson({ TabWidth, UseTab })).replace(/\"/g, '\\"'); + return JSON.stringify(styleJson({ TabWidth, UseTab })).replace( + /[\\"]/g, + '\\$&' + ); } function styleJson({ From 676eb2f588a82eaf06b6b591f4dbb79a243e6954 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 2 Aug 2022 21:42:20 -0700 Subject: [PATCH 07/14] Escape special characters in formatter configuration for Windows The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a command line argument. Previously, the contents of this string were not given any special treatment to ensure compatibility with the command interpreter used on Windows machines. That did not result in problems only because the configuration didn't contain problematic combinations of characters. This good fortune will not persist through updates to the configuration, so the command must be properly processed. The Windows command interpreter does not use the POSIX style backslash escaping. For this reason, escaped quotes in the argument are recognized as normal quotes, meaning that the string alternates between quoted and unquoted states at random. When a character with special significance to the Windows command interpreter happens to occur outside a quoted section, an error results. The solution is to use the Windows command interpreter's caret escaping on these characters. Since such an escaping system is not recognized by POSIX shells, this is only done when the application is running on a Windows machine. References: - https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/echo#remarks - https://en.wikipedia.org/wiki/Escape_character#Windows_Command_Prompt --- .../src/node/clang-formatter.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index d7bdeb9e2..48375836c 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -1,3 +1,4 @@ +import * as os from 'os'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { MaybePromise } from '@theia/core/lib/common/types'; import { FileUri } from '@theia/core/lib/node/file-uri'; @@ -123,10 +124,23 @@ function toClangOptions( // See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { - return JSON.stringify(styleJson({ TabWidth, UseTab })).replace( + let styleArgument = JSON.stringify(styleJson({ TabWidth, UseTab })).replace( /[\\"]/g, '\\$&' ); + if (os.platform() === 'win32') { + // Windows command interpreter does not use backslash escapes. This causes the argument to have alternate quoted and + // unquoted sections. + // Special characters in the unquoted sections must be caret escaped. + const styleArgumentSplit = styleArgument.split('"'); + for (let i = 1; i < styleArgumentSplit.length; i += 2) { + styleArgumentSplit[i] = styleArgumentSplit[i].replace(/[<>^|]/g, '^$&'); + } + + styleArgument = styleArgumentSplit.join('"'); + } + + return styleArgument; } function styleJson({ From 54db9bbce83a6ee75829c3e6658755271b308f94 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 1 Aug 2022 07:03:06 -0700 Subject: [PATCH 08/14] Sync sketch formatter configuration from source The Arduino IDE's "Auto Format" feature is configured to produce the standard Arduino sketch formatting style, as established by the Arduino IDE 1.x formatter. The configuration is consumed by several other projects which require the configuration in a YAML file. In order to provide all the consumers with a single canonical source and to locate the infrastructure and activity related to the maintenance of the file in a more appropriate repository, it is now hosted in a permanent location in the `arduino/tooling-project-assets` repository. The following changes have been made to the source configuration: - Move documentation comments to a dedicated file in the upstream repository - Make additional non-functional changes to the configuration format to facilitate maintenance - Update to use the configuration API of ClangFormat 14.0.0 This last item did result in some functional changes to the configuration which will result in minor differences in the formatter output. These are actually reversions of unwanted differences from the Arduino IDE 1.x formatter output, which were unavoidable when using the 11.0.1 version of ClangFormat in use at the time of the configuration's creation. These changes will provide greater consistency during the migration from Arduino IDE 1.x to 2.x. The default output of the Arduino IDE 1.x formatter will continue to be considered the "gold standard" until Arduino IDE 2.x graduates from "pre-release" status. The Arduino IDE 2.x formatter configuration is fully customizable according to the preferences of each user. Those already using custom configurations will not be affected in any way (though they are encouraged to sync their configuration files from the source to bring them into compliance with the configuration API of the ClangFormat version currently in use by Arduino IDE 2.x). See the documentation and commit history for the source file for details on the configuration changes: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration --- .../src/node/clang-formatter.ts | 127 ++++++++++++------ 1 file changed, 83 insertions(+), 44 deletions(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index 48375836c..2431d96d6 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -122,7 +122,6 @@ function toClangOptions( return { UseTab: 'Never', TabWidth: 2 }; } -// See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { let styleArgument = JSON.stringify(styleJson({ TabWidth, UseTab })).replace( /[\\"]/g, @@ -147,16 +146,15 @@ function styleJson({ TabWidth, UseTab, }: ClangFormatOptions): Record { + // Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration return { - Language: 'Cpp', - // # LLVM is the default style setting, used when a configuration option is not set here - BasedOnStyle: 'LLVM', AccessModifierOffset: -2, AlignAfterOpenBracket: 'Align', - AlignConsecutiveAssignments: false, - AlignConsecutiveBitFields: false, - AlignConsecutiveDeclarations: false, - AlignConsecutiveMacros: false, + AlignArrayOfStructures: 'None', + AlignConsecutiveAssignments: 'None', + AlignConsecutiveBitFields: 'None', + AlignConsecutiveDeclarations: 'None', + AlignConsecutiveMacros: 'None', AlignEscapedNewlines: 'DontAlign', AlignOperands: 'Align', AlignTrailingComments: true, @@ -167,16 +165,18 @@ function styleJson({ AllowShortCaseLabelsOnASingleLine: true, AllowShortEnumsOnASingleLine: true, AllowShortFunctionsOnASingleLine: 'Empty', - AllowShortIfStatementsOnASingleLine: 'Always', + AllowShortIfStatementsOnASingleLine: 'AllIfsAndElse', AllowShortLambdasOnASingleLine: 'Empty', AllowShortLoopsOnASingleLine: true, AlwaysBreakAfterDefinitionReturnType: 'None', AlwaysBreakAfterReturnType: 'None', AlwaysBreakBeforeMultilineStrings: false, AlwaysBreakTemplateDeclarations: 'No', + AttributeMacros: ['__capability'], + BasedOnStyle: 'LLVM', BinPackArguments: true, BinPackParameters: true, - // # Only used when "BreakBeforeBraces" set to "Custom" + BitFieldColonSpacing: 'Both', BraceWrapping: { AfterCaseLabel: false, AfterClass: false, @@ -184,7 +184,7 @@ function styleJson({ AfterEnum: false, AfterFunction: false, AfterNamespace: false, - // #AfterObjCDeclaration: + AfterObjCDeclaration: false, AfterStruct: false, AfterUnion: false, AfterExternBlock: false, @@ -193,104 +193,143 @@ function styleJson({ BeforeLambdaBody: false, BeforeWhile: false, IndentBraces: false, - SplitEmptyFunction: false, - SplitEmptyRecord: false, - SplitEmptyNamespace: false, + SplitEmptyFunction: true, + SplitEmptyRecord: true, + SplitEmptyNamespace: true, }, - // # Java-specific - // #BreakAfterJavaFieldAnnotations: + BreakAfterJavaFieldAnnotations: false, BreakBeforeBinaryOperators: 'NonAssignment', BreakBeforeBraces: 'Attach', + BreakBeforeConceptDeclarations: false, + BreakBeforeInheritanceComma: false, BreakBeforeTernaryOperators: true, BreakConstructorInitializers: 'BeforeColon', + BreakConstructorInitializersBeforeComma: false, BreakInheritanceList: 'BeforeColon', BreakStringLiterals: false, ColumnLimit: 0, - // # "" matches none CommentPragmas: '', CompactNamespaces: false, - ConstructorInitializerAllOnOneLineOrOnePerLine: true, + ConstructorInitializerAllOnOneLineOrOnePerLine: false, ConstructorInitializerIndentWidth: 2, ContinuationIndentWidth: 2, Cpp11BracedListStyle: false, DeriveLineEnding: true, DerivePointerAlignment: true, DisableFormat: false, - // # Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". - // #ExperimentalAutoDetectBinPacking: + EmptyLineAfterAccessModifier: 'Leave', + EmptyLineBeforeAccessModifier: 'Leave', + ExperimentalAutoDetectBinPacking: false, FixNamespaceComments: false, - ForEachMacros: [], + ForEachMacros: ['foreach', 'Q_FOREACH', 'BOOST_FOREACH'], + IfMacros: ['KJ_IF_MAYBE'], IncludeBlocks: 'Preserve', - IncludeCategories: [], - // # "" matches none + IncludeCategories: [ + { + Regex: '^"(llvm|llvm-c|clang|clang-c)/', + Priority: 2, + SortPriority: 0, + CaseSensitive: false, + }, + { + Regex: '^(<|"(gtest|gmock|isl|json)/)', + Priority: 3, + SortPriority: 0, + CaseSensitive: false, + }, + { Regex: '.*', Priority: 1, SortPriority: 0, CaseSensitive: false }, + ], IncludeIsMainRegex: '', IncludeIsMainSourceRegex: '', + IndentAccessModifiers: false, IndentCaseBlocks: true, IndentCaseLabels: true, IndentExternBlock: 'Indent', IndentGotoLabels: false, IndentPPDirectives: 'None', + IndentRequires: true, IndentWidth: 2, IndentWrappedFunctionNames: false, InsertTrailingCommas: 'None', - // # Java-specific - // #JavaImportGroups: - // # JavaScript-specific - // #JavaScriptQuotes: - // #JavaScriptWrapImports + JavaScriptQuotes: 'Leave', + JavaScriptWrapImports: true, KeepEmptyLinesAtTheStartOfBlocks: true, + LambdaBodyIndentation: 'Signature', + Language: 'Cpp', MacroBlockBegin: '', MacroBlockEnd: '', - // # Set to a large number to effectively disable MaxEmptyLinesToKeep: 100000, NamespaceIndentation: 'None', - NamespaceMacros: [], - // # Objective C-specific - // #ObjCBinPackProtocolList: - // #ObjCBlockIndentWidth: - // #ObjCBreakBeforeNestedBlockParam: - // #ObjCSpaceAfterProperty: - // #ObjCSpaceBeforeProtocolList + ObjCBinPackProtocolList: 'Auto', + ObjCBlockIndentWidth: 2, + ObjCBreakBeforeNestedBlockParam: true, + ObjCSpaceAfterProperty: false, + ObjCSpaceBeforeProtocolList: true, + PPIndentWidth: -1, + PackConstructorInitializers: 'BinPack', PenaltyBreakAssignment: 1, PenaltyBreakBeforeFirstCallParameter: 1, PenaltyBreakComment: 1, PenaltyBreakFirstLessLess: 1, + PenaltyBreakOpenParenthesis: 1, PenaltyBreakString: 1, PenaltyBreakTemplateDeclaration: 1, PenaltyExcessCharacter: 1, + PenaltyIndentedWhitespace: 1, PenaltyReturnTypeOnItsOwnLine: 1, - // # Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) PointerAlignment: 'Right', - RawStringFormats: [], + QualifierAlignment: 'Leave', + ReferenceAlignment: 'Pointer', ReflowComments: false, - SortIncludes: false, + RemoveBracesLLVM: false, + SeparateDefinitionBlocks: 'Leave', + ShortNamespaceLines: 0, + SortIncludes: 'Never', + SortJavaStaticImport: 'Before', SortUsingDeclarations: false, SpaceAfterCStyleCast: false, SpaceAfterLogicalNot: false, SpaceAfterTemplateKeyword: false, + SpaceAroundPointerQualifiers: 'Default', SpaceBeforeAssignmentOperators: true, + SpaceBeforeCaseColon: false, SpaceBeforeCpp11BracedList: false, SpaceBeforeCtorInitializerColon: true, SpaceBeforeInheritanceColon: true, SpaceBeforeParens: 'ControlStatements', + SpaceBeforeParensOptions: { + AfterControlStatements: true, + AfterForeachMacros: true, + AfterFunctionDefinitionName: false, + AfterFunctionDeclarationName: false, + AfterIfMacros: true, + AfterOverloadedOperator: false, + BeforeNonEmptyParentheses: false, + }, SpaceBeforeRangeBasedForLoopColon: true, SpaceBeforeSquareBrackets: false, SpaceInEmptyBlock: false, SpaceInEmptyParentheses: false, SpacesBeforeTrailingComments: 2, - SpacesInAngles: false, + SpacesInAngles: 'Leave', SpacesInCStyleCastParentheses: false, SpacesInConditionalStatement: false, SpacesInContainerLiterals: false, + SpacesInLineCommentPrefix: { Minimum: 0, Maximum: -1 }, SpacesInParentheses: false, SpacesInSquareBrackets: false, Standard: 'Auto', - StatementMacros: [], + StatementAttributeLikeMacros: ['Q_EMIT'], + StatementMacros: ['Q_UNUSED', 'QT_REQUIRE_VERSION'], TabWidth, - TypenameMacros: [], - // # Default to LF if line endings can't be detected from the content (DeriveLineEnding). UseCRLF: false, UseTab, - WhitespaceSensitiveMacros: [], + WhitespaceSensitiveMacros: [ + 'STRINGIZE', + 'PP_STRINGIZE', + 'BOOST_PP_STRINGIZE', + 'NS_SWIFT_NAME', + 'CF_SWIFT_NAME', + ], }; } From aebec0f9422fe0dd654d66dcadd7bf211e1e1d47 Mon Sep 17 00:00:00 2001 From: Francesco Spissu <94986937+francescospissu@users.noreply.github.com> Date: Tue, 9 Aug 2022 14:40:56 +0200 Subject: [PATCH 09/14] Live change of theme from Preferences dropdown (#1296) --- .../browser/dialogs/settings/settings-component.tsx | 10 ++++------ .../src/browser/dialogs/settings/settings-dialog.tsx | 12 ++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx index 6bbc3c4c1..dc3d00252 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx @@ -201,12 +201,7 @@ export class SettingsComponent extends React.Component<