From f9641a3d76d46d1d9a52809bf22e58aea38bc869 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 13 Aug 2019 09:09:29 +0200 Subject: [PATCH 01/15] Initial support of the default paths from the CLI. Signed-off-by: Akos Kitta --- .vscode/tasks.json | 12 +-- arduino-ide-browser/package.json | 2 +- arduino-ide-electron/package.json | 2 +- .../browser/arduino-frontend-contribution.tsx | 19 ----- .../src/node/arduino-backend-module.ts | 11 +++ .../src/node/arduino-cli-contribution.ts | 27 +++++++ arduino-ide-extension/src/node/arduino-cli.ts | 79 +++++++++++++++++++ .../src/node/arduino-daemon.ts | 30 +++---- .../src/node/core-client-provider-impl.ts | 40 +++++----- package.json | 6 +- 10 files changed, 161 insertions(+), 67 deletions(-) create mode 100644 arduino-ide-extension/src/node/arduino-cli-contribution.ts create mode 100644 arduino-ide-extension/src/node/arduino-cli.ts diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9ce4065b..9f66b9c2 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Arduino-PoC - Start Browser Example", + "label": "Arduino Editor - Start Browser Example", "type": "shell", "command": "yarn --cwd ./arduino-ide-browser start", "group": "build", @@ -15,7 +15,7 @@ } }, { - "label": "Arduino-PoC - Watch Theia Extension", + "label": "Arduino Editor - Watch Theia Extension", "type": "shell", "command": "yarn --cwd ./arduino-ide-extension watch", "group": "build", @@ -26,7 +26,7 @@ } }, { - "label": "Arduino-PoC - Watch Browser Example", + "label": "Arduino Editor - Watch Browser Example", "type": "shell", "command": "yarn --cwd ./arduino-ide-browser watch", "group": "build", @@ -37,11 +37,11 @@ } }, { - "label": "Arduino-PoC - Watch All", + "label": "Arduino Editor - Watch All", "type": "shell", "dependsOn": [ - "Arduino-PoC - Watch Theia Extension", - "Arduino-PoC - Watch Browser Example" + "Arduino Editor - Watch Theia Extension", + "Arduino Editor - Watch Browser Example" ] } ] diff --git a/arduino-ide-browser/package.json b/arduino-ide-browser/package.json index afa5811d..04d45922 100644 --- a/arduino-ide-browser/package.json +++ b/arduino-ide-browser/package.json @@ -30,7 +30,7 @@ "theia": { "frontend": { "config": { - "applicationName": "Arduino-PoC", + "applicationName": "Arduino Editor", "defaultTheme": "arduino-theme", "preferences": { "editor.autoSave": "on" diff --git a/arduino-ide-electron/package.json b/arduino-ide-electron/package.json index f3392091..ed1b2639 100644 --- a/arduino-ide-electron/package.json +++ b/arduino-ide-electron/package.json @@ -33,7 +33,7 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Arduino-PoC", + "applicationName": "Arduino Editor", "defaultTheme": "arduino-theme", "preferences": { "editor.autoSave": "on" diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index e62c192e..0872fc39 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -543,25 +543,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C return undefined; } - // private async onNoBoardsInstalled() { - // const action = await this.messageService.info("You have no boards installed. Use the boards manager to install one.", "Open Boards Manager"); - // if (!action) { - // return; - // } - - // this.boardsListWidgetFrontendContribution.openView({ reveal: true }); - // } - - // private async onUnknownBoard() { - // const action = await this.messageService.warn("There's a board connected for which you need to install software." + - // " If this were not a PoC we would offer you the right package now.", "Open Boards Manager"); - // if (!action) { - // return; - // } - - // this.boardsListWidgetFrontendContribution.openView({ reveal: true }); - // } - private isArduinoToolbar(maybeToolbarWidget: any): boolean { if (maybeToolbarWidget instanceof ArduinoToolbar) { return true; diff --git a/arduino-ide-extension/src/node/arduino-backend-module.ts b/arduino-ide-extension/src/node/arduino-backend-module.ts index 3d50f866..d6226a09 100644 --- a/arduino-ide-extension/src/node/arduino-backend-module.ts +++ b/arduino-ide-extension/src/node/arduino-backend-module.ts @@ -22,8 +22,19 @@ import { SketchesService, SketchesServicePath } from '../common/protocol/sketche import { MonitorServiceImpl } from './monitor/monitor-service-impl'; import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service'; import { MonitorClientProvider } from './monitor/monitor-client-provider'; +import { ArduinoCli } from './arduino-cli'; +import { ArduinoCliContribution } from './arduino-cli-contribution'; +import { CliContribution } from '@theia/core/lib/node'; export default new ContainerModule((bind, unbind, isBound, rebind) => { + // Theia backend CLI contribution. + bind(ArduinoCliContribution).toSelf().inSingletonScope(); + bind(CliContribution).toService(ArduinoCliContribution); + + // Provides the path of the Ardunio CLI. + bind(ArduinoCli).toSelf().inSingletonScope(); + + // Shared daemonn bind(ArduinoDaemon).toSelf().inSingletonScope(); bind(BackendApplicationContribution).toService(ArduinoDaemon); diff --git a/arduino-ide-extension/src/node/arduino-cli-contribution.ts b/arduino-ide-extension/src/node/arduino-cli-contribution.ts new file mode 100644 index 00000000..148b92b5 --- /dev/null +++ b/arduino-ide-extension/src/node/arduino-cli-contribution.ts @@ -0,0 +1,27 @@ +import { injectable } from 'inversify'; +import { Argv, Arguments } from 'yargs'; +import { CliContribution } from '@theia/core/lib/node'; + +@injectable() +export class ArduinoCliContribution implements CliContribution { + + protected _debugCli = false + + configure(conf: Argv): void { + conf.option('debug-cli', { + description: 'Can be specified if the CLI daemon process was started externally.', + type: 'boolean', + default: false, + nargs: 1 + }); + } + + setArguments(args: Arguments): void { + this._debugCli = args['debug-cli']; + } + + get debugCli(): boolean { + return this._debugCli; + } + +} diff --git a/arduino-ide-extension/src/node/arduino-cli.ts b/arduino-ide-extension/src/node/arduino-cli.ts new file mode 100644 index 00000000..66514f92 --- /dev/null +++ b/arduino-ide-extension/src/node/arduino-cli.ts @@ -0,0 +1,79 @@ +import * as os from 'os'; +import * as which from 'which'; +import * as cp from 'child_process'; +import { join, delimiter } from 'path'; +import { injectable } from 'inversify'; + +@injectable() +export class ArduinoCli { + + async getExecPath(): Promise { + const build = join(__dirname, '..', '..', 'build'); + return new Promise((resolve, reject) => { + which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => { + if (err) { + reject(err); + return; + } + resolve(path); + }); + }); + } + + async getDefaultConfig(): Promise { + const command = await this.getExecPath(); + return new Promise((resolve, reject) => { + cp.execFile( + command, + ['config', 'dump', '--format', 'json'], + { encoding: 'utf8' }, + (error, stdout, stderr) => { + + if (error) { + throw error; + } + + if (stderr) { + throw new Error(stderr); + } + + // const { sketchbook_path: sketchDirPath, arduino_data: dataDirPath } = JSON.parse(raw); + + // https://github.com/arduino/arduino-cli/issues/342 + // XXX: this is a hack. The CLI provides a non-valid JSON. + const config: Partial = {}; + const raw = stdout.trim(); + for (const line of raw.split(/\r?\n/) || []) { + // TODO: Named capture groups are avail from ES2018. + // const pair = line.match(/(?[^:]+):(?[^,]+),?/); + const pair = line.split(':').map(entry => entry.trim()); + if (pair[0] === 'sketchbook_path') { + config.sketchDirPath = pair[1]; + } else if (pair[0] === 'arduino_data') { + config.dataDirPath = pair[1]; + } + } + + if (!config.dataDirPath) { + reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`)); + return; + } + + if (!config.sketchDirPath) { + reject(new Error(`Could not parse config. 'sketchbook_path' was missing from: ${stdout}`)); + return; + } + + resolve({ sketchDirPath: config.sketchDirPath, dataDirPath: config.dataDirPath }); + }); + }); + } + +} + +export namespace ArduinoCli { + export interface Config { + sketchDirPath: string; + dataDirPath: string; + } +} diff --git a/arduino-ide-extension/src/node/arduino-daemon.ts b/arduino-ide-extension/src/node/arduino-daemon.ts index 767a3452..9141eca6 100644 --- a/arduino-ide-extension/src/node/arduino-daemon.ts +++ b/arduino-ide-extension/src/node/arduino-daemon.ts @@ -1,6 +1,4 @@ -import * as which from 'which'; -import * as os from 'os'; -import { join, delimiter } from 'path'; +import { join } from 'path'; import { exec, spawn, SpawnOptions } from 'child_process'; import { inject, injectable, named } from 'inversify'; import { ILogger } from '@theia/core/lib/common/logger'; @@ -9,17 +7,22 @@ import { Deferred } from '@theia/core/lib/common/promise-util'; import { environment } from '@theia/application-package/lib/environment'; import { DaemonLog } from './daemon-log'; import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; +import { ArduinoCliContribution } from './arduino-cli-contribution'; +import { ArduinoCli } from './arduino-cli'; @injectable() export class ArduinoDaemon implements BackendApplicationContribution { - // Set this to `true` if you want to connect to a CLI running in debug mode. - static DEBUG_CLI = false; - @inject(ILogger) @named('daemon') protected readonly logger: ILogger + @inject(ArduinoCli) + protected readonly cli: ArduinoCli; + + @inject(ArduinoCliContribution) + protected readonly cliContribution: ArduinoCliContribution; + @inject(ToolOutputServiceServer) protected readonly toolOutputService: ToolOutputServiceServer; @@ -27,17 +30,8 @@ export class ArduinoDaemon implements BackendApplicationContribution { async onStart() { try { - if (!ArduinoDaemon.DEBUG_CLI) { - const build = join(__dirname, '..', '..', 'build'); - const executable = await new Promise((resolve, reject) => { - which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => { - if (err) { - reject(err); - return; - } - resolve(path); - }); - }); + if (!this.cliContribution.debugCli) { + const executable = await this.cli.getExecPath(); this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`); const daemon = exec(`${executable} --debug daemon`, (err, stdout, stderr) => { if (err || stderr) { @@ -74,7 +68,7 @@ export class ArduinoDaemon implements BackendApplicationContribution { await new Promise(resolve => setTimeout(resolve, 2000)); this.isReady.resolve(); - if (!ArduinoDaemon.DEBUG_CLI) { + if (!this.cliContribution.debugCli) { this.logger.info(`<<< The 'arduino-cli' daemon is up an running.`); } else { this.logger.info(`Assuming the 'arduino-cli' already runs in debug mode.`); diff --git a/arduino-ide-extension/src/node/core-client-provider-impl.ts b/arduino-ide-extension/src/node/core-client-provider-impl.ts index f259259b..a796c9af 100644 --- a/arduino-ide-extension/src/node/core-client-provider-impl.ts +++ b/arduino-ide-extension/src/node/core-client-provider-impl.ts @@ -1,5 +1,11 @@ -import { inject, injectable } from 'inversify'; +import * as fs from 'fs'; import * as grpc from '@grpc/grpc-js'; +import * as PQueue from 'p-queue'; +import { inject, injectable } from 'inversify'; +import URI from '@theia/core/lib/common/uri'; +import { FileSystem } from '@theia/filesystem/lib/common'; +import { WorkspaceServiceExt } from '../browser/workspace-service-ext'; +import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb'; import { InitResp, @@ -10,16 +16,9 @@ import { UpdateLibrariesIndexReq, UpdateLibrariesIndexResp } from './cli-protocol/commands/commands_pb'; -import { WorkspaceServiceExt } from '../browser/workspace-service-ext'; -import { FileSystem } from '@theia/filesystem/lib/common'; -import URI from '@theia/core/lib/common/uri'; -import { CoreClientProvider, Client } from './core-client-provider'; -import * as PQueue from 'p-queue'; -import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; +import { ArduinoCli } from './arduino-cli'; import { Instance } from './cli-protocol/commands/common_pb'; -import * as fs from 'fs-extra'; -import * as path from 'path'; -import * as os from 'os'; +import { CoreClientProvider, Client } from './core-client-provider'; @injectable() export class CoreClientProviderImpl implements CoreClientProvider { @@ -36,6 +35,9 @@ export class CoreClientProviderImpl implements CoreClientProvider { @inject(ToolOutputServiceServer) protected readonly toolOutputService: ToolOutputServiceServer; + @inject(ArduinoCli) + protected readonly cli: ArduinoCli; + async getClient(workspaceRootOrResourceUri?: string): Promise { return this.clientRequestQueue.add(() => new Promise(async resolve => { const roots = await this.workspaceServiceExt.roots(); @@ -76,19 +78,19 @@ export class CoreClientProviderImpl implements CoreClientProvider { throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`); } - const defaultDownloadsDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'downloads'); - if (!fs.existsSync(defaultDownloadsDirPath)) { - fs.mkdirpSync(defaultDownloadsDirPath); + const { dataDirPath, sketchDirPath } = await this.cli.getDefaultConfig(); + + if (!fs.existsSync(dataDirPath)) { + throw new Error(`Data dir path does not exist: ${dataDirPath}.`); } - const defaultDataDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'data') - if (!fs.existsSync(defaultDataDirPath)) { - fs.mkdirpSync(defaultDataDirPath); + if (!fs.existsSync(sketchDirPath)) { + throw new Error(`Sketch dir path does not exist: ${sketchDirPath}.`); } - config.setSketchbookdir(rootPath); - config.setDatadir(defaultDataDirPath); - config.setDownloadsdir(defaultDownloadsDirPath); + config.setSketchbookdir(sketchDirPath); + config.setDatadir(dataDirPath); + config.setDownloadsdir(dataDirPath); config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']); const initReq = new InitReq(); diff --git a/package.json b/package.json index 86a71718..8c83c919 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "arduino-poc", + "name": "arduino-editor", "version": "0.0.1", - "description": "A PoC demonstrating an Arduino IDE built using Eclipse Theia", + "description": "Arduino IDE built using Eclipse Theia", "main": "index.js", "repository": "https://github.com/bcmi-labs/arduino-editor.git", "author": "Christian Weichel ", @@ -14,7 +14,7 @@ "prepare": "lerna run prepare", "rebuild:browser": "theia rebuild:browser", "rebuild:electron": "theia rebuild:electron", - "start": "cd arduino-ide-browser && yarn start", + "start": "yarn --cwd ./arduino-ide-browser start", "watch": "lerna run watch --parallel" }, "workspaces": [ From d809daa20ae581436d7e80dabbd934e349309a8f Mon Sep 17 00:00:00 2001 From: jbicker Date: Fri, 23 Aug 2019 18:30:13 +0200 Subject: [PATCH 02/15] Use sketch folder as workspace Signed-off-by: jbicker --- .../browser/arduino-frontend-contribution.tsx | 37 +++++++++++++------ .../src/browser/sketch-factory.ts | 7 +++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 0872fc39..3915f297 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -133,21 +133,16 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - + @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; + @inject(WorkspaceService) + protected readonly workspaceService: WorkspaceService; + protected boardsToolbarItem: BoardsToolBarItem | null; protected wsSketchCount: number = 0; - constructor(@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService) { - this.workspaceService.onWorkspaceChanged(() => { - if (this.workspaceService.workspace) { - this.registerSketchesInMenu(this.menuRegistry); - } - }) - } - @postConstruct() protected async init(): Promise { // This is a hack. Otherwise, the backend services won't bind. @@ -161,6 +156,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C } this.boardsServiceClient.onBoardsConfigChanged(updateStatusBar); updateStatusBar(this.boardsServiceClient.boardsConfig); + + this.registerSketchesInMenu(this.menuRegistry); } registerToolbarItems(registry: TabBarToolbarRegistry): void { @@ -453,7 +450,18 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C } protected async getWorkspaceSketches(): Promise { - const sketches = this.sketches.getSketches(this.workspaceService.workspace); + + let sketches: Sketch[] = []; + const userHome = await this.fileSystem.getCurrentUserHome(); + console.log('userHome', userHome); + if (!!userHome) { + // TODO: get this from the backend + const result = new URI(userHome.uri).resolve('Arduino-PoC').resolve('Sketches').toString(); + const stat = await this.fileSystem.getFileStat(result); + if (!!stat) { + sketches = await this.sketches.getSketches(stat); + } + } return sketches; } @@ -461,13 +469,18 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C const url = new URL(window.location.href); const currentSketch = url.searchParams.get('sketch'); // Nothing to do if we want to open the same sketch which is already opened. - if (!!currentSketch && new URI(currentSketch).toString() === new URI(uri).toString()) { - this.messageService.info(`The '${this.labelProvider.getLongName(new URI(uri))}' is already opened.`); + const sketchUri = new URI(uri); + if (!!currentSketch && new URI(currentSketch).toString() === sketchUri.toString()) { + this.messageService.info(`The '${this.labelProvider.getLongName(sketchUri)}' is already opened.`); // NOOP. return; } // Preserve the current window if the `sketch` is not in the `searchParams`. url.searchParams.set('sketch', uri); + const hash = await this.fileSystem.getFsPath(sketchUri.toString()); + if (hash) { + url.hash = hash; + } if (!currentSketch) { setTimeout(() => window.location.href = url.toString(), 100); return; diff --git a/arduino-ide-extension/src/browser/sketch-factory.ts b/arduino-ide-extension/src/browser/sketch-factory.ts index efb70981..364c0d6b 100644 --- a/arduino-ide-extension/src/browser/sketch-factory.ts +++ b/arduino-ide-extension/src/browser/sketch-factory.ts @@ -38,7 +38,8 @@ export class SketchFactory { const sketchDir = parent.resolve(sketchName); const sketchFile = sketchDir.resolve(`${sketchName}.ino`); this.fileSystem.createFolder(sketchDir.toString()); - this.fileSystem.createFile(sketchFile.toString(), { content: ` + this.fileSystem.createFile(sketchFile.toString(), { + content: ` void setup() { // put your setup code here, to run once: @@ -51,6 +52,10 @@ void loop() { ` }); const location = new URL(window.location.href); location.searchParams.set('sketch', sketchFile.toString()); + const hash = await this.fileSystem.getFsPath(sketchDir.toString()); + if (hash) { + location.hash = hash; + } this.windowService.openNewWindow(location.toString()); } catch (e) { throw new Error("Cannot create new sketch: " + e); From d5589c435f4fe1c6421d8aa90d7df83ba187f4e4 Mon Sep 17 00:00:00 2001 From: jbicker Date: Mon, 26 Aug 2019 15:59:18 +0200 Subject: [PATCH 03/15] Get the default sketchbook path from backend Signed-off-by: jbicker --- .../browser/arduino-frontend-contribution.tsx | 12 +++++++----- .../src/browser/arduino-frontend-module.ts | 4 ++++ .../src/browser/arduino-workspace-service.ts | 13 ++++++------- .../src/common/protocol/config-service.ts | 12 ++++++++++++ .../src/node/arduino-backend-module.ts | 11 +++++++++++ arduino-ide-extension/src/node/arduino-cli.ts | 16 +++++----------- .../src/node/config-service-impl.ts | 14 ++++++++++++++ .../src/node/default-workspace-server-ext.ts | 10 ++++++---- 8 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 arduino-ide-extension/src/common/protocol/config-service.ts create mode 100644 arduino-ide-extension/src/node/config-service-impl.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 3915f297..87b953ca 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -46,6 +46,7 @@ import { BoardsConfigDialog } from './boards/boards-config-dialog'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; import { BoardsConfig } from './boards/boards-config'; import { MonitorService } from '../common/protocol/monitor-service'; +import { ConfigService } from '../common/protocol/config-service'; export namespace ArduinoMenus { export const SKETCH = [...MAIN_MENU_BAR, '3_sketch']; @@ -140,6 +141,9 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; + @inject(ConfigService) + protected readonly configService: ConfigService; + protected boardsToolbarItem: BoardsToolBarItem | null; protected wsSketchCount: number = 0; @@ -452,11 +456,9 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C protected async getWorkspaceSketches(): Promise { let sketches: Sketch[] = []; - const userHome = await this.fileSystem.getCurrentUserHome(); - console.log('userHome', userHome); - if (!!userHome) { - // TODO: get this from the backend - const result = new URI(userHome.uri).resolve('Arduino-PoC').resolve('Sketches').toString(); + const config = await this.configService.getConfiguration(); + const result = config.sketchDirPath; + if (!!result) { const stat = await this.fileSystem.getFileStat(result); if (!!stat) { sketches = await this.sketches.getSketches(stat); diff --git a/arduino-ide-extension/src/browser/arduino-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-frontend-module.ts index 2cd6b476..4e74e2c9 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-frontend-module.ts @@ -56,6 +56,7 @@ import { LibraryItemRenderer } from './library/library-item-renderer'; import { BoardItemRenderer } from './boards/boards-item-renderer'; import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl'; import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service'; +import { ConfigService, ConfigServicePath } from '../common/protocol/config-service'; const ElementQueries = require('css-element-queries/src/ElementQueries'); if (!ARDUINO_PRO_MODE) { @@ -96,6 +97,9 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un // Sketch list service bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope(); + // Config service + bind(ConfigService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ConfigServicePath)).inSingletonScope(); + // Boards service bind(BoardsService).toDynamicValue(context => { const connection = context.container.get(WebSocketConnectionProvider); diff --git a/arduino-ide-extension/src/browser/arduino-workspace-service.ts b/arduino-ide-extension/src/browser/arduino-workspace-service.ts index 304a33e8..f40857ee 100644 --- a/arduino-ide-extension/src/browser/arduino-workspace-service.ts +++ b/arduino-ide-extension/src/browser/arduino-workspace-service.ts @@ -4,6 +4,7 @@ import { WorkspaceServer } from "@theia/workspace/lib/common"; import { FileSystem, FileStat } from "@theia/filesystem/lib/common"; import URI from "@theia/core/lib/common/uri"; import { SketchFactory } from "./sketch-factory"; +import { ConfigService } from "../common/protocol/config-service"; /** * This is workaround to have custom frontend binding for the default workspace, although we @@ -21,16 +22,14 @@ export class AWorkspaceService extends WorkspaceService { @inject(SketchFactory) protected readonly sketchFactory: SketchFactory; + @inject(ConfigService) + protected readonly configService: ConfigService; + protected async getDefaultWorkspacePath(): Promise { let result = await super.getDefaultWorkspacePath(); if (!result) { - const userHome = await this.fileSystem.getCurrentUserHome(); - if (!userHome) { - return; - } - - // The backend has created this location if it was missing. - result = new URI(userHome.uri).resolve('Arduino-PoC').resolve('Sketches').toString(); + const config = await this.configService.getConfiguration(); + result = config.sketchDirPath; } const stat = await this.fileSystem.getFileStat(result); diff --git a/arduino-ide-extension/src/common/protocol/config-service.ts b/arduino-ide-extension/src/common/protocol/config-service.ts new file mode 100644 index 00000000..6cc69d1a --- /dev/null +++ b/arduino-ide-extension/src/common/protocol/config-service.ts @@ -0,0 +1,12 @@ + +export const ConfigServicePath = '/services/config-service'; +export const ConfigService = Symbol('ConfigService'); + +export interface ConfigService { + getConfiguration(): Promise; +} + +export interface Config { + sketchDirPath: string; + dataDirPath: string; +} \ No newline at end of file diff --git a/arduino-ide-extension/src/node/arduino-backend-module.ts b/arduino-ide-extension/src/node/arduino-backend-module.ts index d6226a09..daeb3878 100644 --- a/arduino-ide-extension/src/node/arduino-backend-module.ts +++ b/arduino-ide-extension/src/node/arduino-backend-module.ts @@ -19,12 +19,14 @@ import { DefaultWorkspaceServerExt } from './default-workspace-server-ext'; import { WorkspaceServer } from '@theia/workspace/lib/common'; import { SketchesServiceImpl } from './sketches-service-impl'; import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service'; +import { ConfigService, ConfigServicePath } from '../common/protocol/config-service'; import { MonitorServiceImpl } from './monitor/monitor-service-impl'; import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service'; import { MonitorClientProvider } from './monitor/monitor-client-provider'; import { ArduinoCli } from './arduino-cli'; import { ArduinoCliContribution } from './arduino-cli-contribution'; import { CliContribution } from '@theia/core/lib/node'; +import { ConfigServiceImpl } from './config-service-impl'; export default new ContainerModule((bind, unbind, isBound, rebind) => { // Theia backend CLI contribution. @@ -53,6 +55,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bindBackendService(SketchesServicePath, SketchesService); }); bind(ConnectionContainerModule).toConstantValue(sketchesServiceConnectionModule); + + bind(ConfigServiceImpl).toSelf().inSingletonScope(); + bind(ConfigService).toService(ConfigServiceImpl); + + // Config service + const configServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { + bindBackendService(ConfigServicePath, ConfigService); + }); + bind(ConnectionContainerModule).toConstantValue(configServiceConnectionModule); // Boards service const boardsServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { diff --git a/arduino-ide-extension/src/node/arduino-cli.ts b/arduino-ide-extension/src/node/arduino-cli.ts index 66514f92..007f9f7d 100644 --- a/arduino-ide-extension/src/node/arduino-cli.ts +++ b/arduino-ide-extension/src/node/arduino-cli.ts @@ -3,6 +3,7 @@ import * as which from 'which'; import * as cp from 'child_process'; import { join, delimiter } from 'path'; import { injectable } from 'inversify'; +import { Config } from '../common/protocol/config-service'; @injectable() export class ArduinoCli { @@ -20,9 +21,9 @@ export class ArduinoCli { }); } - async getDefaultConfig(): Promise { + async getDefaultConfig(): Promise { const command = await this.getExecPath(); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { cp.execFile( command, ['config', 'dump', '--format', 'json'], @@ -41,7 +42,7 @@ export class ArduinoCli { // https://github.com/arduino/arduino-cli/issues/342 // XXX: this is a hack. The CLI provides a non-valid JSON. - const config: Partial = {}; + const config: Partial = {}; const raw = stdout.trim(); for (const line of raw.split(/\r?\n/) || []) { // TODO: Named capture groups are avail from ES2018. @@ -69,11 +70,4 @@ export class ArduinoCli { }); } -} - -export namespace ArduinoCli { - export interface Config { - sketchDirPath: string; - dataDirPath: string; - } -} +} \ No newline at end of file diff --git a/arduino-ide-extension/src/node/config-service-impl.ts b/arduino-ide-extension/src/node/config-service-impl.ts new file mode 100644 index 00000000..f869fd9a --- /dev/null +++ b/arduino-ide-extension/src/node/config-service-impl.ts @@ -0,0 +1,14 @@ +import { injectable, inject } from "inversify"; +import { ConfigService, Config } from "../common/protocol/config-service"; +import { ArduinoCli } from "./arduino-cli"; + +@injectable() +export class ConfigServiceImpl implements ConfigService { + + @inject(ArduinoCli) + protected readonly cli: ArduinoCli; + + async getConfiguration(): Promise { + return this.cli.getDefaultConfig(); + } +} \ No newline at end of file diff --git a/arduino-ide-extension/src/node/default-workspace-server-ext.ts b/arduino-ide-extension/src/node/default-workspace-server-ext.ts index c747408e..a5a17e1b 100644 --- a/arduino-ide-extension/src/node/default-workspace-server-ext.ts +++ b/arduino-ide-extension/src/node/default-workspace-server-ext.ts @@ -1,14 +1,16 @@ -import * as os from 'os'; -import * as path from 'path'; -import { injectable } from 'inversify'; +import { injectable, inject } from 'inversify'; import { FileUri } from '@theia/core/lib/node/file-uri'; import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server'; +import { ConfigService } from '../common/protocol/config-service'; @injectable() export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer { + @inject(ConfigService) protected readonly configService: ConfigService; + protected async getWorkspaceURIFromCli(): Promise { - return FileUri.create(path.join(os.homedir(), 'Arduino-PoC', 'Sketches')).toString(); + const config = await this.configService.getConfiguration(); + return FileUri.create(config.sketchDirPath).toString(); } } \ No newline at end of file From 41c603937cf222e4d45fc11aaa9a0e91d90a437d Mon Sep 17 00:00:00 2001 From: jbicker Date: Mon, 26 Aug 2019 16:51:30 +0200 Subject: [PATCH 04/15] Setting download dir to {dataDir}/staging; Create data and sketch folders if they don't exist. Signed-off-by: jbicker --- .../src/node/core-client-provider-impl.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arduino-ide-extension/src/node/core-client-provider-impl.ts b/arduino-ide-extension/src/node/core-client-provider-impl.ts index a796c9af..4c28df7e 100644 --- a/arduino-ide-extension/src/node/core-client-provider-impl.ts +++ b/arduino-ide-extension/src/node/core-client-provider-impl.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import * as path from 'path'; import * as grpc from '@grpc/grpc-js'; import * as PQueue from 'p-queue'; import { inject, injectable } from 'inversify'; @@ -81,16 +82,21 @@ export class CoreClientProviderImpl implements CoreClientProvider { const { dataDirPath, sketchDirPath } = await this.cli.getDefaultConfig(); if (!fs.existsSync(dataDirPath)) { - throw new Error(`Data dir path does not exist: ${dataDirPath}.`); + fs.mkdirSync(dataDirPath); } if (!fs.existsSync(sketchDirPath)) { - throw new Error(`Sketch dir path does not exist: ${sketchDirPath}.`); + fs.mkdirSync(sketchDirPath); + } + + const downloadDir = path.join(dataDirPath, 'staging'); + if (fs.existsSync(downloadDir)) { + fs.mkdirSync(downloadDir); } config.setSketchbookdir(sketchDirPath); config.setDatadir(dataDirPath); - config.setDownloadsdir(dataDirPath); + config.setDownloadsdir(downloadDir); config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']); const initReq = new InitReq(); From 9ae721292dac675c7f7a59f62ecbe1798e2cda41 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 27 Aug 2019 18:27:12 +0200 Subject: [PATCH 05/15] Fixed the FS path issue on Windows. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 9 ++---- .../src/browser/arduino-workspace-service.ts | 2 +- .../src/common/protocol/config-service.ts | 7 ++--- arduino-ide-extension/src/node/arduino-cli.ts | 31 +++++++++++++------ .../src/node/core-client-provider-impl.ts | 7 +++-- .../src/node/default-workspace-server-ext.ts | 3 +- 6 files changed, 35 insertions(+), 24 deletions(-) diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 87b953ca..9a778948 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -457,12 +457,9 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C let sketches: Sketch[] = []; const config = await this.configService.getConfiguration(); - const result = config.sketchDirPath; - if (!!result) { - const stat = await this.fileSystem.getFileStat(result); - if (!!stat) { - sketches = await this.sketches.getSketches(stat); - } + const stat = await this.fileSystem.getFileStat(config.sketchDirUri); + if (!!stat) { + sketches = await this.sketches.getSketches(stat); } return sketches; } diff --git a/arduino-ide-extension/src/browser/arduino-workspace-service.ts b/arduino-ide-extension/src/browser/arduino-workspace-service.ts index f40857ee..e47c6899 100644 --- a/arduino-ide-extension/src/browser/arduino-workspace-service.ts +++ b/arduino-ide-extension/src/browser/arduino-workspace-service.ts @@ -29,7 +29,7 @@ export class AWorkspaceService extends WorkspaceService { let result = await super.getDefaultWorkspacePath(); if (!result) { const config = await this.configService.getConfiguration(); - result = config.sketchDirPath; + result = config.sketchDirUri; } const stat = await this.fileSystem.getFileStat(result); diff --git a/arduino-ide-extension/src/common/protocol/config-service.ts b/arduino-ide-extension/src/common/protocol/config-service.ts index 6cc69d1a..fb8c90cd 100644 --- a/arduino-ide-extension/src/common/protocol/config-service.ts +++ b/arduino-ide-extension/src/common/protocol/config-service.ts @@ -1,4 +1,3 @@ - export const ConfigServicePath = '/services/config-service'; export const ConfigService = Symbol('ConfigService'); @@ -7,6 +6,6 @@ export interface ConfigService { } export interface Config { - sketchDirPath: string; - dataDirPath: string; -} \ No newline at end of file + sketchDirUri: string; + dataDirUri: string; +} diff --git a/arduino-ide-extension/src/node/arduino-cli.ts b/arduino-ide-extension/src/node/arduino-cli.ts index 007f9f7d..db559f3c 100644 --- a/arduino-ide-extension/src/node/arduino-cli.ts +++ b/arduino-ide-extension/src/node/arduino-cli.ts @@ -2,12 +2,17 @@ import * as os from 'os'; import * as which from 'which'; import * as cp from 'child_process'; import { join, delimiter } from 'path'; -import { injectable } from 'inversify'; +import { injectable, inject } from 'inversify'; +import { ILogger } from '@theia/core'; +import { FileUri } from '@theia/core/lib/node/file-uri'; import { Config } from '../common/protocol/config-service'; @injectable() export class ArduinoCli { + @inject(ILogger) + protected logger: ILogger; + async getExecPath(): Promise { const build = join(__dirname, '..', '..', 'build'); return new Promise((resolve, reject) => { @@ -47,25 +52,33 @@ export class ArduinoCli { for (const line of raw.split(/\r?\n/) || []) { // TODO: Named capture groups are avail from ES2018. // const pair = line.match(/(?[^:]+):(?[^,]+),?/); - const pair = line.split(':').map(entry => entry.trim()); - if (pair[0] === 'sketchbook_path') { - config.sketchDirPath = pair[1]; - } else if (pair[0] === 'arduino_data') { - config.dataDirPath = pair[1]; + const index = line.indexOf(':'); + if (index !== -1) { + const key = line.substr(0, index).trim(); + const value = line.substr(index + 1, line.length).trim(); + if (!!key && !!value) { + if (key === 'sketchbook_path') { + config.sketchDirUri = FileUri.create(value).toString(); + } else if (key === 'arduino_data') { + config.dataDirUri = FileUri.create(value).toString(); + } + } } } - if (!config.dataDirPath) { + if (!config.dataDirUri) { reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`)); return; } - if (!config.sketchDirPath) { + if (!config.sketchDirUri) { reject(new Error(`Could not parse config. 'sketchbook_path' was missing from: ${stdout}`)); return; } - resolve({ sketchDirPath: config.sketchDirPath, dataDirPath: config.dataDirPath }); + this.logger.info(`Retrieved the default configuration from the CLI: ${JSON.stringify(config)}`); + + resolve({ sketchDirUri: config.sketchDirUri, dataDirUri: config.dataDirUri }); }); }); } diff --git a/arduino-ide-extension/src/node/core-client-provider-impl.ts b/arduino-ide-extension/src/node/core-client-provider-impl.ts index 4c28df7e..a4dba8b9 100644 --- a/arduino-ide-extension/src/node/core-client-provider-impl.ts +++ b/arduino-ide-extension/src/node/core-client-provider-impl.ts @@ -20,6 +20,7 @@ import { import { ArduinoCli } from './arduino-cli'; import { Instance } from './cli-protocol/commands/common_pb'; import { CoreClientProvider, Client } from './core-client-provider'; +import { FileUri } from '@theia/core/lib/node'; @injectable() export class CoreClientProviderImpl implements CoreClientProvider { @@ -79,7 +80,9 @@ export class CoreClientProviderImpl implements CoreClientProvider { throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`); } - const { dataDirPath, sketchDirPath } = await this.cli.getDefaultConfig(); + const { dataDirUri, sketchDirUri } = await this.cli.getDefaultConfig(); + const dataDirPath = FileUri.fsPath(dataDirUri); + const sketchDirPath = FileUri.fsPath(sketchDirUri); if (!fs.existsSync(dataDirPath)) { fs.mkdirSync(dataDirPath); @@ -90,7 +93,7 @@ export class CoreClientProviderImpl implements CoreClientProvider { } const downloadDir = path.join(dataDirPath, 'staging'); - if (fs.existsSync(downloadDir)) { + if (!fs.existsSync(downloadDir)) { fs.mkdirSync(downloadDir); } diff --git a/arduino-ide-extension/src/node/default-workspace-server-ext.ts b/arduino-ide-extension/src/node/default-workspace-server-ext.ts index a5a17e1b..70a0a66b 100644 --- a/arduino-ide-extension/src/node/default-workspace-server-ext.ts +++ b/arduino-ide-extension/src/node/default-workspace-server-ext.ts @@ -1,5 +1,4 @@ import { injectable, inject } from 'inversify'; -import { FileUri } from '@theia/core/lib/node/file-uri'; import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server'; import { ConfigService } from '../common/protocol/config-service'; @@ -10,7 +9,7 @@ export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer { protected async getWorkspaceURIFromCli(): Promise { const config = await this.configService.getConfiguration(); - return FileUri.create(config.sketchDirPath).toString(); + return config.sketchDirUri; } } \ No newline at end of file From b82d5e4f0be9997377dbc60f6e21b7cb2242acce Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Wed, 28 Aug 2019 08:13:11 +0000 Subject: [PATCH 06/15] Use sketch directory as url param when a new created sketch gets opened Signed-off-by: Jan Bicker --- arduino-ide-extension/src/browser/sketch-factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arduino-ide-extension/src/browser/sketch-factory.ts b/arduino-ide-extension/src/browser/sketch-factory.ts index 364c0d6b..2a50aa61 100644 --- a/arduino-ide-extension/src/browser/sketch-factory.ts +++ b/arduino-ide-extension/src/browser/sketch-factory.ts @@ -51,7 +51,7 @@ void loop() { } ` }); const location = new URL(window.location.href); - location.searchParams.set('sketch', sketchFile.toString()); + location.searchParams.set('sketch', sketchDir.toString()); const hash = await this.fileSystem.getFsPath(sketchDir.toString()); if (hash) { location.hash = hash; From cd94608aee2e0337ebd8673c1efdec81a65f8b9d Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 29 Aug 2019 08:05:47 +0200 Subject: [PATCH 07/15] Renamed the applications. Signed-off-by: Akos Kitta --- .gitignore | 3 ++- .gitpod.yml | 2 +- .vscode/launch.json | 6 +++--- .vscode/tasks.json | 4 ++-- README.md | 2 +- {arduino-ide-browser => browser-app}/package.json | 2 +- {arduino-ide-electron => electron-app}/package.json | 2 +- electron/packager/index.js | 4 ++-- package.json | 8 ++++---- 9 files changed, 17 insertions(+), 16 deletions(-) rename {arduino-ide-browser => browser-app}/package.json (97%) rename {arduino-ide-electron => electron-app}/package.json (97%) diff --git a/.gitignore b/.gitignore index ddaa0df3..f9597d13 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,8 @@ build/ downloads/ !electron/build/ src-gen/ -arduino-ide-*/webpack.config.js +browser-app/webpack.config.js +electron-app/webpack.config.js .DS_Store /workspace/static # switching from `electron` to `browser` in dev mode. diff --git a/.gitpod.yml b/.gitpod.yml index 5205cf06..a74a845c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -8,7 +8,7 @@ ports: tasks: - init: > yarn && - yarn --cwd ./arduino-ide-browser start + yarn --cwd ./browser-app start github: prebuilds: diff --git a/.vscode/launch.json b/.vscode/launch.json index da9bf2ac..c2791d75 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,7 +14,7 @@ "type": "node", "request": "launch", "name": "Launch Backend", - "program": "${workspaceRoot}/arduino-ide-browser/src-gen/backend/main.js", + "program": "${workspaceRoot}/browser-app/src-gen/backend/main.js", "args": [ "--hostname=0.0.0.0", "--port=3000", @@ -26,8 +26,8 @@ }, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/arduino-ide-browser/src-gen/backend/*.js", - "${workspaceRoot}/arduino-ide-browser/lib/**/*.js", + "${workspaceRoot}/browser-app/src-gen/backend/*.js", + "${workspaceRoot}/browser-app/lib/**/*.js", "${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js" ], "smartStep": true, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9f66b9c2..80a54749 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "label": "Arduino Editor - Start Browser Example", "type": "shell", - "command": "yarn --cwd ./arduino-ide-browser start", + "command": "yarn --cwd ./browser-app start", "group": "build", "presentation": { "reveal": "always", @@ -28,7 +28,7 @@ { "label": "Arduino Editor - Watch Browser Example", "type": "shell", - "command": "yarn --cwd ./arduino-ide-browser watch", + "command": "yarn --cwd ./browser-app watch", "group": "build", "presentation": { "reveal": "always", diff --git a/README.md b/README.md index 30c85685..aff79893 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ yarn rebuild:browser ``` Then you can start the browser example again: ``` -yarn --cwd arduino-ide-browser start +yarn --cwd browser-app start ``` ## Arduino-PoC Electron Application diff --git a/arduino-ide-browser/package.json b/browser-app/package.json similarity index 97% rename from arduino-ide-browser/package.json rename to browser-app/package.json index 04d45922..d016f717 100644 --- a/arduino-ide-browser/package.json +++ b/browser-app/package.json @@ -1,6 +1,6 @@ { "private": true, - "name": "arduino-ide-browser", + "name": "browser-app", "version": "0.0.1", "license": "MIT", "dependencies": { diff --git a/arduino-ide-electron/package.json b/electron-app/package.json similarity index 97% rename from arduino-ide-electron/package.json rename to electron-app/package.json index ed1b2639..b7d19c3a 100644 --- a/arduino-ide-electron/package.json +++ b/electron-app/package.json @@ -1,6 +1,6 @@ { "private": true, - "name": "arduino-ide-electron", + "name": "electron-app", "version": "0.0.1", "license": "MIT", "dependencies": { diff --git a/electron/packager/index.js b/electron/packager/index.js index 0307df8c..049f1487 100644 --- a/electron/packager/index.js +++ b/electron/packager/index.js @@ -47,13 +47,13 @@ } //-----------------------------------------------------+ - // No need to build the `arduino-ide-browser` example. | + // No need to build the `browser-app` example. | //-----------------------------------------------------+ //@ts-ignore let pkg = require('../working-copy/package.json'); const workspaces = pkg.workspaces; // We cannot remove the `arduino-ide-electron`. Otherwise, there is not way to collect the unused dependencies. - const dependenciesToRemove = ['arduino-ide-browser']; + const dependenciesToRemove = ['browser-app']; for (const dependencyToRemove of dependenciesToRemove) { const index = workspaces.indexOf(dependencyToRemove); if (index !== -1) { diff --git a/package.json b/package.json index 8c83c919..f011e244 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,12 @@ "prepare": "lerna run prepare", "rebuild:browser": "theia rebuild:browser", "rebuild:electron": "theia rebuild:electron", - "start": "yarn --cwd ./arduino-ide-browser start", + "start": "yarn --cwd ./browser-app start", "watch": "lerna run watch --parallel" }, "workspaces": [ - "arduino-ide-electron", - "arduino-ide-browser", - "arduino-ide-extension" + "arduino-ide-extension", + "electron-app", + "browser-app" ] } From c6311ecb1d472c9828df70eb97ca0cc6248ea381 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 29 Aug 2019 09:17:44 +0200 Subject: [PATCH 08/15] Adapted the CLI download script. Signed-off-by: Akos Kitta --- arduino-ide-extension/package.json | 5 +- arduino-ide-extension/scripts/download-cli.js | 47 ++++++++++--------- yarn.lock | 6 +-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 537c4b45..861026a0 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -12,6 +12,7 @@ "@theia/core": "next", "@theia/editor": "next", "@theia/filesystem": "next", + "@theia/git": "next", "@theia/languages": "next", "@theia/markers": "next", "@theia/monaco": "next", @@ -19,7 +20,6 @@ "@theia/workspace": "next", "@theia/navigator": "next", "@theia/terminal": "next", - "@theia/git": "next", "@theia/search-in-workspace": "next", "@types/ps-tree": "^1.1.0", "@types/which": "^1.3.1", @@ -40,11 +40,12 @@ }, "devDependencies": { "decompress": "^4.2.0", - "decompress-tarbz2": "^4.1.1", + "decompress-targz": "^4.1.1", "decompress-unzip": "^4.0.1", "download": "^7.1.0", "grpc-tools": "^1.7.3", "grpc_tools_node_protoc_ts": "^2.5.0", + "moment": "^2.24.0", "ncp": "^2.0.0", "rimraf": "^2.6.1", "shelljs": "^0.8.3", diff --git a/arduino-ide-extension/scripts/download-cli.js b/arduino-ide-extension/scripts/download-cli.js index 27bd76e5..e7d229d8 100755 --- a/arduino-ide-extension/scripts/download-cli.js +++ b/arduino-ide-extension/scripts/download-cli.js @@ -1,11 +1,16 @@ // @ts-check -// The links to the downloads as of today (11.08.) are the followings: -// - https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli-nightly-latest-${FILE_NAME} -// - https://downloads.arduino.cc/arduino-cli/arduino-cli-latest-${FILE_NAME} +// The links to the downloads as of today (19.08.) are the followings: +// In order to get the latest nightly build for your platform use the following links replacing with the current date, using the format YYYYMMDD (i.e for 2019/Aug/06 use 20190806 ) +// Linux 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Linux_64bit.tar.gz +// Linux ARM 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Linux_ARM64.tar.gz +// Windows 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Windows_64bit.zip +// Mac OSX: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_macOS_64bit.tar.gz (async () => { - const DEFAULT_VERSION = 'nightly'; + // TODO: currently, the download dates are one day behind. + // https://typefox.slack.com/archives/CJJHJCJSJ/p1567062276016400 + const DEFAULT_VERSION = require('moment')().subtract(1, 'day').format('YYYYMMDD'); const os = require('os'); const fs = require('fs'); @@ -14,7 +19,7 @@ const download = require('download'); const decompress = require('decompress'); const unzip = require('decompress-unzip'); - const untarbz = require('decompress-tarbz2'); + const untargz = require('decompress-targz'); process.on('unhandledRejection', (reason, _) => { shell.echo(String(reason)); @@ -31,11 +36,7 @@ .option('cli-version', { alias: 'cv', default: DEFAULT_VERSION, - choices: [ - // 'latest', // TODO: How do we get the source for `latest`. Currently, `latest` is the `0.3.7-alpha.preview`. - 'nightly' - ], - describe: `The version of the 'arduino-cli' to download. Defaults to ${DEFAULT_VERSION}.` + describe: `The version of the 'arduino-cli' to download with the YYYYMMDD format. Defaults to ${DEFAULT_VERSION}.` }) .option('force-download', { alias: 'fd', @@ -68,13 +69,12 @@ const suffix = (() => { switch (platform) { - case 'darwin': return 'macosx.zip'; - case 'win32': return 'windows.zip'; + case 'darwin': return 'macOS_64bit.tar.gz'; + case 'win32': return 'Windows_64bit.zip'; case 'linux': { switch (arch) { - case 'arm64': return 'linuxarm.tar.bz2'; - case 'x32': return 'linux32.tar.bz2'; - case 'x64': return 'linux64.tar.bz2'; + case 'arm64': return 'Linux_ARM64.tar.gz'; + case 'x64': return 'Linux_64bit.tar.gz'; default: return undefined; } } @@ -86,7 +86,7 @@ shell.exit(1); } - const url = `https://downloads.arduino.cc/arduino-cli/${version === 'nightly' ? 'nightly/' : ''}arduino-cli-${version}-latest-${suffix}`; + const url = `https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-${version}_${suffix}`; shell.echo(`>>> Downloading 'arduino-cli' from '${url}'...`); const data = await download(url); shell.echo(`<<< Download succeeded.`); @@ -94,16 +94,21 @@ const files = await decompress(data, downloads, { plugins: [ unzip(), - untarbz() + untargz() ] }); - shell.echo('<<< Decompressing succeeded.'); - - if (files.length !== 1) { + if (files.length === 0) { shell.echo('Error ocurred when decompressing the CLI.'); shell.exit(1); } - if (shell.mv('-f', path.join(downloads, files[0].path), cli).code !== 0) { + const cliIndex = files.findIndex(f => f.path.startsWith('arduino-cli')); + if (cliIndex === -1) { + shell.echo('The downloaded artifact does not contains the CLI.'); + shell.exit(1); + } + shell.echo('<<< Decompressing succeeded.'); + + if (shell.mv('-f', path.join(downloads, files[cliIndex].path), cli).code !== 0) { shell.echo(`Could not move file to ${cli}.`); shell.exit(1); } diff --git a/yarn.lock b/yarn.lock index cdc8943d..ae96ea90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4473,7 +4473,7 @@ decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: is-stream "^1.1.0" tar-stream "^1.5.2" -decompress-tarbz2@^4.0.0, decompress-tarbz2@^4.1.1: +decompress-tarbz2@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== @@ -4484,7 +4484,7 @@ decompress-tarbz2@^4.0.0, decompress-tarbz2@^4.1.1: seek-bzip "^1.0.5" unbzip2-stream "^1.0.9" -decompress-targz@^4.0.0: +decompress-targz@^4.0.0, decompress-targz@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== @@ -8098,7 +8098,7 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.10.6, moment@^2.18.1, moment@^2.21.0: +moment@^2.10.6, moment@^2.18.1, moment@^2.21.0, moment@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== From e636e06a7ee2b41dc8461d2aa1295ee4a08a3de6 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 29 Aug 2019 09:21:51 +0200 Subject: [PATCH 09/15] Download today's CLI. The issues was an interim CI problem. Signed-off-by: Akos Kitta --- arduino-ide-extension/scripts/download-cli.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arduino-ide-extension/scripts/download-cli.js b/arduino-ide-extension/scripts/download-cli.js index e7d229d8..865d67e1 100755 --- a/arduino-ide-extension/scripts/download-cli.js +++ b/arduino-ide-extension/scripts/download-cli.js @@ -8,9 +8,7 @@ (async () => { - // TODO: currently, the download dates are one day behind. - // https://typefox.slack.com/archives/CJJHJCJSJ/p1567062276016400 - const DEFAULT_VERSION = require('moment')().subtract(1, 'day').format('YYYYMMDD'); + const DEFAULT_VERSION = require('moment')().format('YYYYMMDD'); const os = require('os'); const fs = require('fs'); From 9f7aec4091c5886de931e250458bba982874a212 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 29 Aug 2019 09:52:56 +0200 Subject: [PATCH 10/15] Adapted to latest CLI. Signed-off-by: Akos Kitta --- arduino-ide-extension/src/node/arduino-cli.ts | 42 +++++-------------- .../src/node/arduino-daemon.ts | 2 +- .../cli-protocol/commands/compile_pb.d.ts | 4 ++ .../node/cli-protocol/commands/compile_pb.js | 29 ++++++++++++- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/arduino-ide-extension/src/node/arduino-cli.ts b/arduino-ide-extension/src/node/arduino-cli.ts index db559f3c..5168c8cc 100644 --- a/arduino-ide-extension/src/node/arduino-cli.ts +++ b/arduino-ide-extension/src/node/arduino-cli.ts @@ -43,44 +43,24 @@ export class ArduinoCli { throw new Error(stderr); } - // const { sketchbook_path: sketchDirPath, arduino_data: dataDirPath } = JSON.parse(raw); + const { sketchbook_path, arduino_data } = JSON.parse(stdout.trim()); - // https://github.com/arduino/arduino-cli/issues/342 - // XXX: this is a hack. The CLI provides a non-valid JSON. - const config: Partial = {}; - const raw = stdout.trim(); - for (const line of raw.split(/\r?\n/) || []) { - // TODO: Named capture groups are avail from ES2018. - // const pair = line.match(/(?[^:]+):(?[^,]+),?/); - const index = line.indexOf(':'); - if (index !== -1) { - const key = line.substr(0, index).trim(); - const value = line.substr(index + 1, line.length).trim(); - if (!!key && !!value) { - if (key === 'sketchbook_path') { - config.sketchDirUri = FileUri.create(value).toString(); - } else if (key === 'arduino_data') { - config.dataDirUri = FileUri.create(value).toString(); - } - } - } - } - - if (!config.dataDirUri) { - reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`)); - return; - } - - if (!config.sketchDirUri) { + if (!sketchbook_path) { reject(new Error(`Could not parse config. 'sketchbook_path' was missing from: ${stdout}`)); return; } - this.logger.info(`Retrieved the default configuration from the CLI: ${JSON.stringify(config)}`); + if (!arduino_data) { + reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`)); + return; + } - resolve({ sketchDirUri: config.sketchDirUri, dataDirUri: config.dataDirUri }); + resolve({ + sketchDirUri: FileUri.create(sketchbook_path).toString(), + dataDirUri: FileUri.create(arduino_data).toString() + }); }); }); } -} \ No newline at end of file +} diff --git a/arduino-ide-extension/src/node/arduino-daemon.ts b/arduino-ide-extension/src/node/arduino-daemon.ts index 9141eca6..22c1ea6b 100644 --- a/arduino-ide-extension/src/node/arduino-daemon.ts +++ b/arduino-ide-extension/src/node/arduino-daemon.ts @@ -33,7 +33,7 @@ export class ArduinoDaemon implements BackendApplicationContribution { if (!this.cliContribution.debugCli) { const executable = await this.cli.getExecPath(); this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`); - const daemon = exec(`${executable} --debug daemon`, (err, stdout, stderr) => { + const daemon = exec(`${executable} daemon -v --log-level info --format json`, (err, stdout, stderr) => { if (err || stderr) { console.log(err || new Error(stderr)); return; diff --git a/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.d.ts b/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.d.ts index e27969ce..39959cd0 100644 --- a/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.d.ts +++ b/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.d.ts @@ -51,6 +51,9 @@ export class CompileReq extends jspb.Message { getExportfile(): string; setExportfile(value: string): void; + getJobs(): number; + setJobs(value: number): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): CompileReq.AsObject; @@ -77,6 +80,7 @@ export namespace CompileReq { quiet: boolean, vidpid: string, exportfile: string, + jobs: number, } } diff --git a/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.js b/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.js index 338f9d8e..1b3f213f 100644 --- a/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.js +++ b/arduino-ide-extension/src/node/cli-protocol/commands/compile_pb.js @@ -81,7 +81,8 @@ proto.cc.arduino.cli.commands.CompileReq.toObject = function(includeInstance, ms verbose: jspb.Message.getFieldWithDefault(msg, 10, false), quiet: jspb.Message.getFieldWithDefault(msg, 11, false), vidpid: jspb.Message.getFieldWithDefault(msg, 12, ""), - exportfile: jspb.Message.getFieldWithDefault(msg, 13, "") + exportfile: jspb.Message.getFieldWithDefault(msg, 13, ""), + jobs: jspb.Message.getFieldWithDefault(msg, 14, 0) }; if (includeInstance) { @@ -171,6 +172,10 @@ proto.cc.arduino.cli.commands.CompileReq.deserializeBinaryFromReader = function( var value = /** @type {string} */ (reader.readString()); msg.setExportfile(value); break; + case 14: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJobs(value); + break; default: reader.skipField(); break; @@ -292,6 +297,13 @@ proto.cc.arduino.cli.commands.CompileReq.serializeBinaryToWriter = function(mess f ); } + f = message.getJobs(); + if (f !== 0) { + writer.writeInt32( + 14, + f + ); + } }; @@ -527,6 +539,21 @@ proto.cc.arduino.cli.commands.CompileReq.prototype.setExportfile = function(valu }; +/** + * optional int32 jobs = 14; + * @return {number} + */ +proto.cc.arduino.cli.commands.CompileReq.prototype.getJobs = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 14, 0)); +}; + + +/** @param {number} value */ +proto.cc.arduino.cli.commands.CompileReq.prototype.setJobs = function(value) { + jspb.Message.setProto3IntField(this, 14, value); +}; + + /** * Generated by JsPbCodeGenerator. From 98764b56aaaa794da97325b40b8a704a9c6469da Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 30 Aug 2019 10:11:43 +0200 Subject: [PATCH 11/15] Switched to the JSON log format for the daemon. Signed-off-by: Akos Kitta --- .../src/node/arduino-daemon.ts | 2 +- arduino-ide-extension/src/node/daemon-log.ts | 130 ++++++++++++------ 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/arduino-ide-extension/src/node/arduino-daemon.ts b/arduino-ide-extension/src/node/arduino-daemon.ts index 22c1ea6b..b9ee058a 100644 --- a/arduino-ide-extension/src/node/arduino-daemon.ts +++ b/arduino-ide-extension/src/node/arduino-daemon.ts @@ -33,7 +33,7 @@ export class ArduinoDaemon implements BackendApplicationContribution { if (!this.cliContribution.debugCli) { const executable = await this.cli.getExecPath(); this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`); - const daemon = exec(`${executable} daemon -v --log-level info --format json`, (err, stdout, stderr) => { + const daemon = exec(`${executable} daemon -v --log-level info --format json --log-format json`, (err, stdout, stderr) => { if (err || stderr) { console.log(err || new Error(stderr)); return; diff --git a/arduino-ide-extension/src/node/daemon-log.ts b/arduino-ide-extension/src/node/daemon-log.ts index 5c452881..bf754033 100644 --- a/arduino-ide-extension/src/node/daemon-log.ts +++ b/arduino-ide-extension/src/node/daemon-log.ts @@ -8,7 +8,58 @@ export interface DaemonLog { export namespace DaemonLog { - export type Level = 'info' | 'debug' | 'warning' | 'error'; + export interface Url { + readonly Scheme: string; + readonly Host: string; + readonly Path: string; + } + + export namespace Url { + + export function is(arg: any | undefined): arg is Url { + return !!arg + && typeof arg.Scheme === 'string' + && typeof arg.Host === 'string' + && typeof arg.Path === 'string'; + } + + export function toString(url: Url): string { + const { Scheme, Host, Path } = url; + return `${Scheme}://${Host}${Path}`; + } + + } + + export interface Tool { + readonly version: string; + readonly systems: System[]; + } + + export namespace Tool { + + export function is(arg: any | undefined): arg is Tool { + return !!arg && typeof arg.version === 'string' && 'systems' in arg; + } + + export function toString(tool: Tool): string { + const { version, systems } = tool; + return `Version: ${version}${!!systems ? ` Systems: [${tool.systems.map(System.toString).join(', ')}]` : ''}`; + } + + } + + export interface System { + readonly os: string; + // readonly Resource: Resource; + } + + export namespace System { + export function toString(system: System): string { + return `OS: ${system.os}` + } + } + + export type Level = 'trace' | 'debug' | 'info' | 'warning' | 'error'; export function is(arg: any | undefined): arg is DaemonLog { return !!arg @@ -20,61 +71,62 @@ export namespace DaemonLog { export function toLogLevel(log: DaemonLog): LogLevel { const { level } = log; switch (level) { - case 'info': return LogLevel.INFO; + case 'trace': return LogLevel.TRACE; case 'debug': return LogLevel.DEBUG; - case 'error': return LogLevel.ERROR; + case 'info': return LogLevel.INFO; case 'warning': return LogLevel.WARN; + case 'error': return LogLevel.ERROR; default: return LogLevel.INFO; } } - export function log(logger: ILogger, toLog: string): void { - const segments = toLog.split('time').filter(s => s.trim().length > 0); - for (const segment of segments) { - const maybeDaemonLog = parse(`time${segment}`.trim()); - for (const logMsg of maybeDaemonLog) { - logger.log(toLogLevel(logMsg), logMsg.msg); - } + export function log(logger: ILogger, logMessages: string): void { + const parsed = parse(logMessages); + for (const log of parsed) { + logger.log(toLogLevel(log), toMessage(log).trim()); // XXX: `trim` as `toMessage` appends a NL. } } - // Super naive. function parse(toLog: string): DaemonLog[] { - const messages = toLog.split('\ntime='); + const messages = toLog.trim().split('\n'); const result: DaemonLog[] = []; for (let i = 0; i < messages.length; i++) { - const msg = (i > 0 ? 'time=' : '') + messages[i]; - const rawSegments = msg.split(/(\s+)/) - .map(segment => segment.replace(/['"]+/g, '')) - .map(segment => segment.trim()) - .filter(segment => segment.length > 0); - - const timeIndex = rawSegments.findIndex(segment => segment.startsWith('time=')); - const levelIndex = rawSegments.findIndex(segment => segment.startsWith('level=')); - const msgIndex = rawSegments.findIndex(segment => segment.startsWith('msg=')); - if (rawSegments.length > 2 - && timeIndex !== -1 - && levelIndex !== -1 - && msgIndex !== -1) { - result.push({ - time: rawSegments[timeIndex].split('=')[1], - level: rawSegments[levelIndex].split('=')[1] as Level, - msg: [rawSegments[msgIndex].split('=')[1], ...rawSegments.slice(msgIndex + 1)].join(' ') - }); - } else { + try { + const maybeDaemonLog = JSON.parse(messages[i]); + if (DaemonLog.is(maybeDaemonLog)) { + result.push(maybeDaemonLog); + continue; + } + } catch { /* NOOP */ } result.push({ - time: new Date().toString(), - level: 'info', - msg: msg + time: new Date().toString(), + level: 'info', + msg: messages[i] }); - } } - // Otherwise, log the string as is. return result; } - export function toPrettyString(logMessage: string): string { - const parsed = parse(logMessage); - return parsed.map(msg => `[${msg.level.toUpperCase() || 'INFO'}] ${msg.msg}\n`).join(''); + export function toPrettyString(logMessages: string, logger?: ILogger): string { + const parsed = parse(logMessages); + return parsed.map(toMessage).join(''); } + + function toMessage(log: DaemonLog): string { + const details = Object.keys(log).filter(key => key !== 'msg' && key !== 'level' && key !== 'time').map(key => toDetails(log, key)).join(', '); + return `[${log.level.toUpperCase()}] ${log.msg}${!!details ? ` [${details}]` : ''}\n` + } + + function toDetails(log: DaemonLog, key: string): string { + let value = (log as any)[key]; + if (DaemonLog.Url.is(value)) { + value = DaemonLog.Url.toString(value); + } else if (DaemonLog.Tool.is(value)) { + value = DaemonLog.Tool.toString(value); + } else if (typeof value === 'object') { + value = JSON.stringify(value).replace(/\"([^(\")"]+)\":/g, '$1:'); + } + return `${key.toLowerCase()}: ${value}`; + } + } \ No newline at end of file From 9298a8cc550987d4ca75693cf86b936767a452ae Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 30 Aug 2019 10:26:13 +0200 Subject: [PATCH 12/15] Log clean-up. Signed-off-by: Akos Kitta --- arduino-ide-extension/src/node/daemon-log.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arduino-ide-extension/src/node/daemon-log.ts b/arduino-ide-extension/src/node/daemon-log.ts index bf754033..fb3f4419 100644 --- a/arduino-ide-extension/src/node/daemon-log.ts +++ b/arduino-ide-extension/src/node/daemon-log.ts @@ -83,7 +83,7 @@ export namespace DaemonLog { export function log(logger: ILogger, logMessages: string): void { const parsed = parse(logMessages); for (const log of parsed) { - logger.log(toLogLevel(log), toMessage(log).trim()); // XXX: `trim` as `toMessage` appends a NL. + logger.log(toLogLevel(log), toMessage(log)); } } @@ -107,14 +107,14 @@ export namespace DaemonLog { return result; } - export function toPrettyString(logMessages: string, logger?: ILogger): string { + export function toPrettyString(logMessages: string): string { const parsed = parse(logMessages); - return parsed.map(toMessage).join(''); + return parsed.map(toMessage).join('\n'); } function toMessage(log: DaemonLog): string { const details = Object.keys(log).filter(key => key !== 'msg' && key !== 'level' && key !== 'time').map(key => toDetails(log, key)).join(', '); - return `[${log.level.toUpperCase()}] ${log.msg}${!!details ? ` [${details}]` : ''}\n` + return `[${log.level.toUpperCase()}] ${log.msg}${!!details ? ` [${details}]` : ''}` } function toDetails(log: DaemonLog, key: string): string { @@ -124,7 +124,7 @@ export namespace DaemonLog { } else if (DaemonLog.Tool.is(value)) { value = DaemonLog.Tool.toString(value); } else if (typeof value === 'object') { - value = JSON.stringify(value).replace(/\"([^(\")"]+)\":/g, '$1:'); + value = JSON.stringify(value).replace(/\"([^(\")"]+)\":/g, '$1:'); // Remove the quotes from the property keys. } return `${key.toLowerCase()}: ${value}`; } From e6e042c8563b13e8181c8623ee8707a683fc28a0 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 30 Aug 2019 10:41:05 +0200 Subject: [PATCH 13/15] Moved `System` declaration before its use-site. Signed-off-by: Akos Kitta --- arduino-ide-extension/src/node/daemon-log.ts | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/arduino-ide-extension/src/node/daemon-log.ts b/arduino-ide-extension/src/node/daemon-log.ts index fb3f4419..0824ffe8 100644 --- a/arduino-ide-extension/src/node/daemon-log.ts +++ b/arduino-ide-extension/src/node/daemon-log.ts @@ -30,24 +30,6 @@ export namespace DaemonLog { } - export interface Tool { - readonly version: string; - readonly systems: System[]; - } - - export namespace Tool { - - export function is(arg: any | undefined): arg is Tool { - return !!arg && typeof arg.version === 'string' && 'systems' in arg; - } - - export function toString(tool: Tool): string { - const { version, systems } = tool; - return `Version: ${version}${!!systems ? ` Systems: [${tool.systems.map(System.toString).join(', ')}]` : ''}`; - } - - } - export interface System { readonly os: string; // readonly Resource: Resource; @@ -59,6 +41,24 @@ export namespace DaemonLog { } } + export interface Tool { + readonly version: string; + readonly systems: System[]; + } + + export namespace Tool { + + export function is(arg: any | undefined): arg is Tool { + return !!arg && typeof arg.version === 'string' && 'systems' in arg; + } + + export function toString(tool: Tool): string { + const { version, systems } = tool; + return `Version: ${version}${!!systems ? ` Systems: [${tool.systems.map(System.toString).join(', ')}]` : ''}`; + } + + } + export type Level = 'trace' | 'debug' | 'info' | 'warning' | 'error'; export function is(arg: any | undefined): arg is DaemonLog { From e8e3c3dc4ce0ef323e401da603c6bfb4724c0f93 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 2 Sep 2019 13:45:36 +0200 Subject: [PATCH 14/15] Use the `latest` CLI, as stated in the doc. Signed-off-by: Akos Kitta --- arduino-ide-extension/scripts/download-cli.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arduino-ide-extension/scripts/download-cli.js b/arduino-ide-extension/scripts/download-cli.js index 865d67e1..62506946 100755 --- a/arduino-ide-extension/scripts/download-cli.js +++ b/arduino-ide-extension/scripts/download-cli.js @@ -1,14 +1,16 @@ // @ts-check -// The links to the downloads as of today (19.08.) are the followings: +// The links to the downloads as of today (02.09.) are the followings: // In order to get the latest nightly build for your platform use the following links replacing with the current date, using the format YYYYMMDD (i.e for 2019/Aug/06 use 20190806 ) // Linux 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Linux_64bit.tar.gz // Linux ARM 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Linux_ARM64.tar.gz // Windows 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_Windows_64bit.zip // Mac OSX: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-_macOS_64bit.tar.gz +// [...] +// redirecting to latest generated builds by replacing latest with the latest available build date, using the format YYYYMMDD (i.e for 2019/Aug/06 latest is replaced with 20190806 (async () => { - const DEFAULT_VERSION = require('moment')().format('YYYYMMDD'); + const DEFAULT_VERSION = 'latest'; // require('moment')().format('YYYYMMDD'); const os = require('os'); const fs = require('fs'); @@ -34,7 +36,7 @@ .option('cli-version', { alias: 'cv', default: DEFAULT_VERSION, - describe: `The version of the 'arduino-cli' to download with the YYYYMMDD format. Defaults to ${DEFAULT_VERSION}.` + describe: `The version of the 'arduino-cli' to download with the YYYYMMDD format, or 'latest'. Defaults to ${DEFAULT_VERSION}.` }) .option('force-download', { alias: 'fd', From 6d590cd11159dfa3dd56f75909179efa7f21fa83 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 2 Sep 2019 14:57:28 +0200 Subject: [PATCH 15/15] Fixed the packager for the new folder structure. Signed-off-by: Akos Kitta --- README.md | 2 +- electron/packager/index.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index aff79893..3af44b6f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ git clone https://github.com/bcmi-labs/arduino-editor cd arduino-editor yarn yarn rebuild:electron -yarn --cwd arduino-ide-electron start +yarn --cwd electron-app start ``` If you want to switch back to the browser-based example, execute the following in the repository root diff --git a/electron/packager/index.js b/electron/packager/index.js index 049f1487..330a4720 100644 --- a/electron/packager/index.js +++ b/electron/packager/index.js @@ -14,7 +14,7 @@ const workingCopy = 'working-copy'; /** - * Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `arduino-ide-electron` folders are. + * Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are. * This could come handy when moving the location of the `electron/packager`. */ const rootPath = join('..', '..'); @@ -42,7 +42,7 @@ // Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. | //----------------------------------------------------------------------------------------------+ mkdir('-p', path('..', workingCopy)); - for (const name of ['arduino-ide-extension', 'arduino-ide-electron', 'yarn.lock', 'package.json', 'lerna.json']) { + for (const name of ['arduino-ide-extension', 'electron-app', 'yarn.lock', 'package.json', 'lerna.json']) { cp('-rf', path(rootPath, name), path('..', workingCopy)); } @@ -52,7 +52,7 @@ //@ts-ignore let pkg = require('../working-copy/package.json'); const workspaces = pkg.workspaces; - // We cannot remove the `arduino-ide-electron`. Otherwise, there is not way to collect the unused dependencies. + // We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies. const dependenciesToRemove = ['browser-app']; for (const dependencyToRemove of dependenciesToRemove) { const index = workspaces.indexOf(dependencyToRemove); @@ -70,13 +70,13 @@ // Collect all unused dependencies by the backend. We have to remove them from the electron app. // The `bundle.js` already contains everything we need for the frontend. // We have to do it before changing the dependencies to `local-path`. - const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/arduino-ide-electron/'); + const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/electron-app/'); //------------------------------------------------------------------------------------+ // Merge the `working-copy/package.json` with `electron/build/template-package.json`. | //------------------------------------------------------------------------------------+ // @ts-ignore - pkg = require('../working-copy/arduino-ide-electron/package.json'); + pkg = require('../working-copy/electron-app/package.json'); // @ts-ignore const template = require('../build/template-package.json'); template.build.files = [ ...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`) ];