mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-13 20:29:27 +00:00
Speed up IDE startup time.
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
@@ -13,7 +13,6 @@ import { environment } from '@theia/application-package/lib/environment';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
||||
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
|
||||
import { DaemonLog } from './daemon-log';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
import { getExecPath, spawnCommand } from './exec-util';
|
||||
|
||||
@@ -32,28 +31,30 @@ export class ArduinoDaemonImpl
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
protected readonly onDaemonStartedEmitter = new Emitter<void>();
|
||||
protected readonly onDaemonStartedEmitter = new Emitter<string>();
|
||||
protected readonly onDaemonStoppedEmitter = new Emitter<void>();
|
||||
|
||||
protected _running = false;
|
||||
protected _ready = new Deferred<void>();
|
||||
protected _port = new Deferred<string>();
|
||||
protected _execPath: string | undefined;
|
||||
protected _port: string;
|
||||
|
||||
// Backend application lifecycle.
|
||||
|
||||
onStart(): void {
|
||||
this.startDaemon();
|
||||
this.startDaemon(); // no await
|
||||
}
|
||||
|
||||
// Daemon API
|
||||
|
||||
async isRunning(): Promise<boolean> {
|
||||
return Promise.resolve(this._running);
|
||||
async getPort(): Promise<string> {
|
||||
return this._port.promise;
|
||||
}
|
||||
|
||||
async getPort(): Promise<string> {
|
||||
return Promise.resolve(this._port);
|
||||
async tryGetPort(): Promise<string | undefined> {
|
||||
if (this._running) {
|
||||
return this._port.promise;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async startDaemon(): Promise<void> {
|
||||
@@ -62,7 +63,6 @@ export class ArduinoDaemonImpl
|
||||
const cliPath = await this.getExecPath();
|
||||
this.onData(`Starting daemon from ${cliPath}...`);
|
||||
const { daemon, port } = await this.spawnDaemonProcess();
|
||||
this._port = port;
|
||||
// Watchdog process for terminating the daemon process when the backend app terminates.
|
||||
spawn(
|
||||
process.execPath,
|
||||
@@ -83,7 +83,7 @@ export class ArduinoDaemonImpl
|
||||
Disposable.create(() => daemon.kill()),
|
||||
Disposable.create(() => this.fireDaemonStopped()),
|
||||
]);
|
||||
this.fireDaemonStarted();
|
||||
this.fireDaemonStarted(port);
|
||||
this.onData('Daemon is running.');
|
||||
} catch (err) {
|
||||
this.onData('Failed to start the daemon.');
|
||||
@@ -103,7 +103,7 @@ export class ArduinoDaemonImpl
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
get onDaemonStarted(): Event<void> {
|
||||
get onDaemonStarted(): Event<string> {
|
||||
return this.onDaemonStartedEmitter.event;
|
||||
}
|
||||
|
||||
@@ -111,10 +111,6 @@ export class ArduinoDaemonImpl
|
||||
return this.onDaemonStoppedEmitter.event;
|
||||
}
|
||||
|
||||
get ready(): Promise<void> {
|
||||
return this._ready.promise;
|
||||
}
|
||||
|
||||
async getExecPath(): Promise<string> {
|
||||
if (this._execPath) {
|
||||
return this._execPath;
|
||||
@@ -240,11 +236,11 @@ export class ArduinoDaemonImpl
|
||||
return ready.promise;
|
||||
}
|
||||
|
||||
protected fireDaemonStarted(): void {
|
||||
protected fireDaemonStarted(port: string): void {
|
||||
this._running = true;
|
||||
this._ready.resolve();
|
||||
this.onDaemonStartedEmitter.fire();
|
||||
this.notificationService.notifyDaemonStarted();
|
||||
this._port.resolve(port);
|
||||
this.onDaemonStartedEmitter.fire(port);
|
||||
this.notificationService.notifyDaemonStarted(port);
|
||||
}
|
||||
|
||||
protected fireDaemonStopped(): void {
|
||||
@@ -252,14 +248,14 @@ export class ArduinoDaemonImpl
|
||||
return;
|
||||
}
|
||||
this._running = false;
|
||||
this._ready.reject(); // Reject all pending.
|
||||
this._ready = new Deferred<void>();
|
||||
this._port.reject(); // Reject all pending.
|
||||
this._port = new Deferred<string>();
|
||||
this.onDaemonStoppedEmitter.fire();
|
||||
this.notificationService.notifyDaemonStopped();
|
||||
}
|
||||
|
||||
protected onData(message: string): void {
|
||||
DaemonLog.log(this.logger, message);
|
||||
this.logger.info(message);
|
||||
}
|
||||
|
||||
protected onError(error: any): void {
|
||||
|
||||
@@ -58,7 +58,10 @@ import {
|
||||
FileSystemExt,
|
||||
FileSystemExtPath,
|
||||
} from '../common/protocol/filesystem-ext';
|
||||
import { ExamplesServiceImpl } from './examples-service-impl';
|
||||
import {
|
||||
BuiltInExamplesServiceImpl,
|
||||
ExamplesServiceImpl,
|
||||
} from './examples-service-impl';
|
||||
import {
|
||||
ExamplesService,
|
||||
ExamplesServicePath,
|
||||
@@ -80,8 +83,6 @@ import {
|
||||
} from '../common/protocol';
|
||||
import { BackendApplication } from './theia/core/backend-application';
|
||||
import { BoardDiscovery } from './board-discovery';
|
||||
import { DefaultGitInit } from './theia/git/git-init';
|
||||
import { GitInit } from '@theia/git/lib/node/init/git-init';
|
||||
import { AuthenticationServiceImpl } from './auth/authentication-service-impl';
|
||||
import {
|
||||
AuthenticationService,
|
||||
@@ -138,6 +139,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
// Built-in examples are not board specific, so it is possible to have one shared instance.
|
||||
bind(BuiltInExamplesServiceImpl).toSelf().inSingletonScope();
|
||||
|
||||
// Examples service. One per backend, each connected FE gets a proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(
|
||||
@@ -329,9 +332,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope()
|
||||
.whenTargetNamed(SerialServiceName);
|
||||
|
||||
bind(DefaultGitInit).toSelf();
|
||||
rebind(GitInit).toService(DefaultGitInit);
|
||||
|
||||
// Remote sketchbook bindings
|
||||
bind(AuthenticationServiceImpl).toSelf().inSingletonScope();
|
||||
bind(AuthenticationService).toService(AuthenticationServiceImpl);
|
||||
|
||||
@@ -55,9 +55,7 @@ export class BoardDiscovery extends CoreClientAware {
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
await this.coreClientProvider.initialized;
|
||||
const coreClient = await this.coreClient();
|
||||
this.startBoardListWatch(coreClient);
|
||||
this.coreClient().then((client) => this.startBoardListWatch(client));
|
||||
}
|
||||
|
||||
stopBoardListWatch(coreClient: CoreClientProvider.Client): Promise<void> {
|
||||
|
||||
@@ -1,42 +1,31 @@
|
||||
import { RecursivePartial } from '@theia/core/lib/common/types';
|
||||
import { Daemon } from '../common/protocol/config-service';
|
||||
import { AdditionalUrls } from '../common/protocol';
|
||||
|
||||
export const CLI_CONFIG = 'arduino-cli.yaml';
|
||||
|
||||
export interface BoardManager {
|
||||
readonly additional_urls: Array<string>;
|
||||
readonly additional_urls: AdditionalUrls;
|
||||
}
|
||||
export namespace BoardManager {
|
||||
export function sameAs(
|
||||
left: RecursivePartial<BoardManager> | undefined,
|
||||
right: RecursivePartial<BoardManager> | undefined
|
||||
): boolean {
|
||||
const leftOrDefault = left || {};
|
||||
const rightOrDefault = right || {};
|
||||
const leftUrls = Array.from(new Set(leftOrDefault.additional_urls || []));
|
||||
const rightUrls = Array.from(new Set(rightOrDefault.additional_urls || []));
|
||||
if (leftUrls.length !== rightUrls.length) {
|
||||
return false;
|
||||
}
|
||||
return leftUrls.every((url) => rightUrls.indexOf(url) !== -1);
|
||||
const leftUrls = left?.additional_urls ?? [];
|
||||
const rightUrls = right?.additional_urls ?? [];
|
||||
return AdditionalUrls.sameAs(leftUrls, rightUrls);
|
||||
}
|
||||
}
|
||||
|
||||
export interface Directories {
|
||||
readonly data: string;
|
||||
readonly downloads: string;
|
||||
readonly user: string;
|
||||
}
|
||||
export namespace Directories {
|
||||
export function is(
|
||||
directories: RecursivePartial<Directories> | undefined
|
||||
): directories is Directories {
|
||||
return (
|
||||
!!directories &&
|
||||
!!directories.data &&
|
||||
!!directories.downloads &&
|
||||
!!directories.user
|
||||
);
|
||||
return !!directories && !!directories.data && !!directories.user;
|
||||
}
|
||||
export function sameAs(
|
||||
left: RecursivePartial<Directories> | undefined,
|
||||
@@ -48,11 +37,7 @@ export namespace Directories {
|
||||
if (right === undefined) {
|
||||
return left === undefined;
|
||||
}
|
||||
return (
|
||||
left.data === right.data &&
|
||||
left.downloads === right.downloads &&
|
||||
left.user === right.user
|
||||
);
|
||||
return left.data === right.data && left.user === right.user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +96,12 @@ export interface CliConfig {
|
||||
// Bare minimum required CLI config.
|
||||
export interface DefaultCliConfig extends CliConfig {
|
||||
directories: Directories;
|
||||
daemon: Daemon;
|
||||
}
|
||||
export namespace DefaultCliConfig {
|
||||
export function is(
|
||||
config: RecursivePartial<DefaultCliConfig> | undefined
|
||||
): config is DefaultCliConfig {
|
||||
return (
|
||||
!!config && Directories.is(config.directories) && Daemon.is(config.daemon)
|
||||
);
|
||||
return !!config && Directories.is(config.directories);
|
||||
}
|
||||
export function sameAs(
|
||||
left: DefaultCliConfig,
|
||||
@@ -127,7 +109,6 @@ export namespace DefaultCliConfig {
|
||||
): boolean {
|
||||
return (
|
||||
Directories.sameAs(left.directories, right.directories) &&
|
||||
Daemon.sameAs(left.daemon, right.daemon) &&
|
||||
BoardManager.sameAs(left.board_manager, right.board_manager) &&
|
||||
Logging.sameAs(left.logging, right.logging)
|
||||
);
|
||||
|
||||
@@ -62,6 +62,12 @@ export class InitRequest extends jspb.Message {
|
||||
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
|
||||
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): InitRequest;
|
||||
|
||||
getProfile(): string;
|
||||
setProfile(value: string): InitRequest;
|
||||
|
||||
getSketchPath(): string;
|
||||
setSketchPath(value: string): InitRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): InitRequest.AsObject;
|
||||
@@ -76,6 +82,8 @@ export class InitRequest extends jspb.Message {
|
||||
export namespace InitRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
profile: string,
|
||||
sketchPath: string,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +101,12 @@ export class InitResponse extends jspb.Message {
|
||||
setError(value?: google_rpc_status_pb.Status): InitResponse;
|
||||
|
||||
|
||||
hasProfile(): boolean;
|
||||
clearProfile(): void;
|
||||
getProfile(): cc_arduino_cli_commands_v1_common_pb.Profile | undefined;
|
||||
setProfile(value?: cc_arduino_cli_commands_v1_common_pb.Profile): InitResponse;
|
||||
|
||||
|
||||
getMessageCase(): InitResponse.MessageCase;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
@@ -109,6 +123,7 @@ export namespace InitResponse {
|
||||
export type AsObject = {
|
||||
initProgress?: InitResponse.Progress.AsObject,
|
||||
error?: google_rpc_status_pb.Status.AsObject,
|
||||
profile?: cc_arduino_cli_commands_v1_common_pb.Profile.AsObject,
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +166,8 @@ export namespace InitResponse {
|
||||
|
||||
ERROR = 2,
|
||||
|
||||
PROFILE = 3,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -866,7 +866,9 @@ proto.cc.arduino.cli.commands.v1.InitRequest.prototype.toObject = function(opt_i
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f)
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
profile: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
sketchPath: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -908,6 +910,14 @@ proto.cc.arduino.cli.commands.v1.InitRequest.deserializeBinaryFromReader = funct
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
|
||||
msg.setInstance(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setProfile(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSketchPath(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -945,6 +955,20 @@ proto.cc.arduino.cli.commands.v1.InitRequest.serializeBinaryToWriter = function(
|
||||
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getProfile();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getSketchPath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -985,6 +1009,42 @@ proto.cc.arduino.cli.commands.v1.InitRequest.prototype.hasInstance = function()
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string profile = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitRequest.prototype.getProfile = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InitRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitRequest.prototype.setProfile = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string sketch_path = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitRequest.prototype.getSketchPath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InitRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitRequest.prototype.setSketchPath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Oneof group definitions for this message. Each group defines the field
|
||||
@@ -994,7 +1054,7 @@ proto.cc.arduino.cli.commands.v1.InitRequest.prototype.hasInstance = function()
|
||||
* @private {!Array<!Array<number>>}
|
||||
* @const
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.oneofGroups_ = [[1,2]];
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.oneofGroups_ = [[1,2,3]];
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
@@ -1002,7 +1062,8 @@ proto.cc.arduino.cli.commands.v1.InitResponse.oneofGroups_ = [[1,2]];
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.MessageCase = {
|
||||
MESSAGE_NOT_SET: 0,
|
||||
INIT_PROGRESS: 1,
|
||||
ERROR: 2
|
||||
ERROR: 2,
|
||||
PROFILE: 3
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1044,7 +1105,8 @@ proto.cc.arduino.cli.commands.v1.InitResponse.prototype.toObject = function(opt_
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
initProgress: (f = msg.getInitProgress()) && proto.cc.arduino.cli.commands.v1.InitResponse.Progress.toObject(includeInstance, f),
|
||||
error: (f = msg.getError()) && google_rpc_status_pb.Status.toObject(includeInstance, f)
|
||||
error: (f = msg.getError()) && google_rpc_status_pb.Status.toObject(includeInstance, f),
|
||||
profile: (f = msg.getProfile()) && cc_arduino_cli_commands_v1_common_pb.Profile.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -1091,6 +1153,11 @@ proto.cc.arduino.cli.commands.v1.InitResponse.deserializeBinaryFromReader = func
|
||||
reader.readMessage(value,google_rpc_status_pb.Status.deserializeBinaryFromReader);
|
||||
msg.setError(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.Profile;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Profile.deserializeBinaryFromReader);
|
||||
msg.setProfile(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -1136,6 +1203,14 @@ proto.cc.arduino.cli.commands.v1.InitResponse.serializeBinaryToWriter = function
|
||||
google_rpc_status_pb.Status.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getProfile();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
3,
|
||||
f,
|
||||
cc_arduino_cli_commands_v1_common_pb.Profile.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1415,6 +1490,43 @@ proto.cc.arduino.cli.commands.v1.InitResponse.prototype.hasError = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional Profile profile = 3;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.Profile}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.prototype.getProfile = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.Profile} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.Profile, 3));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.Profile|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InitResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.prototype.setProfile = function(value) {
|
||||
return jspb.Message.setOneofWrapperField(this, 3, proto.cc.arduino.cli.commands.v1.InitResponse.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InitResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.prototype.clearProfile = function() {
|
||||
return this.setProfile(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.prototype.hasProfile = function() {
|
||||
return jspb.Message.getField(this, 3) != null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -185,28 +185,36 @@ export namespace Platform {
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformReference extends jspb.Message {
|
||||
export class InstalledPlatformReference extends jspb.Message {
|
||||
getId(): string;
|
||||
setId(value: string): PlatformReference;
|
||||
setId(value: string): InstalledPlatformReference;
|
||||
|
||||
getVersion(): string;
|
||||
setVersion(value: string): PlatformReference;
|
||||
setVersion(value: string): InstalledPlatformReference;
|
||||
|
||||
getInstallDir(): string;
|
||||
setInstallDir(value: string): InstalledPlatformReference;
|
||||
|
||||
getPackageUrl(): string;
|
||||
setPackageUrl(value: string): InstalledPlatformReference;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): PlatformReference.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: PlatformReference): PlatformReference.AsObject;
|
||||
toObject(includeInstance?: boolean): InstalledPlatformReference.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: InstalledPlatformReference): InstalledPlatformReference.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: PlatformReference, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): PlatformReference;
|
||||
static deserializeBinaryFromReader(message: PlatformReference, reader: jspb.BinaryReader): PlatformReference;
|
||||
static serializeBinaryToWriter(message: InstalledPlatformReference, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): InstalledPlatformReference;
|
||||
static deserializeBinaryFromReader(message: InstalledPlatformReference, reader: jspb.BinaryReader): InstalledPlatformReference;
|
||||
}
|
||||
|
||||
export namespace PlatformReference {
|
||||
export namespace InstalledPlatformReference {
|
||||
export type AsObject = {
|
||||
id: string,
|
||||
version: string,
|
||||
installDir: string,
|
||||
packageUrl: string,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,3 +242,28 @@ export namespace Board {
|
||||
fqbn: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class Profile extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): Profile;
|
||||
|
||||
getFqbn(): string;
|
||||
setFqbn(value: string): Profile;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Profile.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Profile): Profile.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Profile, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Profile;
|
||||
static deserializeBinaryFromReader(message: Profile, reader: jspb.BinaryReader): Profile;
|
||||
}
|
||||
|
||||
export namespace Profile {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
fqbn: string,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@ var global = Function('return this')();
|
||||
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Board', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgress', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InstalledPlatformReference', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Instance', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Platform', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformReference', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Profile', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Programmer', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.TaskProgress', null, global);
|
||||
/**
|
||||
@@ -137,16 +138,16 @@ if (goog.DEBUG && !COMPILED) {
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference = function(opt_data) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.PlatformReference, jspb.Message);
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.InstalledPlatformReference, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.displayName = 'proto.cc.arduino.cli.commands.v1.PlatformReference';
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.displayName = 'proto.cc.arduino.cli.commands.v1.InstalledPlatformReference';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
@@ -169,6 +170,27 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Board.displayName = 'proto.cc.arduino.cli.commands.v1.Board';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.Profile, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.displayName = 'proto.cc.arduino.cli.commands.v1.Profile';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1405,8 +1427,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.PlatformReference.toObject(opt_includeInstance, this);
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
@@ -1415,14 +1437,16 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.toObject = function
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformReference} msg The msg instance to transform.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.toObject = function(includeInstance, msg) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
version: jspb.Message.getFieldWithDefault(msg, 2, "")
|
||||
version: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
installDir: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
packageUrl: jspb.Message.getFieldWithDefault(msg, 4, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -1436,23 +1460,23 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.toObject = function(includeIn
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference}
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinary = function(bytes) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.PlatformReference;
|
||||
return proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinaryFromReader(msg, reader);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.InstalledPlatformReference;
|
||||
return proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformReference} msg The message object to deserialize into.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference}
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinaryFromReader = function(msg, reader) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
@@ -1467,6 +1491,14 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinaryFromReader =
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setVersion(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setInstallDir(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setPackageUrl(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -1480,9 +1512,9 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinaryFromReader =
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.serializeBinary = function() {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.serializeBinaryToWriter(this, writer);
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
@@ -1490,11 +1522,11 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.serializeBinary = f
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformReference} message
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.serializeBinaryToWriter = function(message, writer) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getId();
|
||||
if (f.length > 0) {
|
||||
@@ -1510,6 +1542,20 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.serializeBinaryToWriter = fun
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getInstallDir();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getPackageUrl();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1517,16 +1563,16 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.serializeBinaryToWriter = fun
|
||||
* optional string id = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.getId = function() {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.getId = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference} returns this
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.setId = function(value) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.setId = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
@@ -1535,20 +1581,56 @@ proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.setId = function(va
|
||||
* optional string version = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.getVersion = function() {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.getVersion = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference} returns this
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.setVersion = function(value) {
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.setVersion = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string install_dir = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.getInstallDir = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.setInstallDir = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string package_url = 4;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.getPackageUrl = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InstalledPlatformReference.prototype.setPackageUrl = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1709,4 +1791,164 @@ proto.cc.arduino.cli.commands.v1.Board.prototype.setFqbn = function(value) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.Profile.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.Profile} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
name: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
fqbn: jspb.Message.getFieldWithDefault(msg, 2, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Profile}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.Profile;
|
||||
return proto.cc.arduino.cli.commands.v1.Profile.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.Profile} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Profile}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setName(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setFqbn(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.Profile.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.Profile} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getName();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getFqbn();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string name = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.getName = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Profile} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.setName = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string fqbn = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.getFqbn = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Profile} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.prototype.setFqbn = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto.cc.arduino.cli.commands.v1);
|
||||
|
||||
@@ -86,6 +86,15 @@ export class CompileRequest extends jspb.Message {
|
||||
setLibraryList(value: Array<string>): CompileRequest;
|
||||
addLibrary(value: string, index?: number): string;
|
||||
|
||||
getKeysKeychain(): string;
|
||||
setKeysKeychain(value: string): CompileRequest;
|
||||
|
||||
getSignKey(): string;
|
||||
setSignKey(value: string): CompileRequest;
|
||||
|
||||
getEncryptKey(): string;
|
||||
setEncryptKey(value: string): CompileRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): CompileRequest.AsObject;
|
||||
@@ -121,6 +130,9 @@ export namespace CompileRequest {
|
||||
sourceOverrideMap: Array<[string, string]>,
|
||||
exportBinaries?: google_protobuf_wrappers_pb.BoolValue.AsObject,
|
||||
libraryList: Array<string>,
|
||||
keysKeychain: string,
|
||||
signKey: string,
|
||||
encryptKey: string,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,14 +163,14 @@ export class CompileResponse extends jspb.Message {
|
||||
|
||||
hasBoardPlatform(): boolean;
|
||||
clearBoardPlatform(): void;
|
||||
getBoardPlatform(): cc_arduino_cli_commands_v1_common_pb.PlatformReference | undefined;
|
||||
setBoardPlatform(value?: cc_arduino_cli_commands_v1_common_pb.PlatformReference): CompileResponse;
|
||||
getBoardPlatform(): cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference | undefined;
|
||||
setBoardPlatform(value?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference): CompileResponse;
|
||||
|
||||
|
||||
hasBuildPlatform(): boolean;
|
||||
clearBuildPlatform(): void;
|
||||
getBuildPlatform(): cc_arduino_cli_commands_v1_common_pb.PlatformReference | undefined;
|
||||
setBuildPlatform(value?: cc_arduino_cli_commands_v1_common_pb.PlatformReference): CompileResponse;
|
||||
getBuildPlatform(): cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference | undefined;
|
||||
setBuildPlatform(value?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference): CompileResponse;
|
||||
|
||||
|
||||
hasProgress(): boolean;
|
||||
@@ -184,8 +196,8 @@ export namespace CompileResponse {
|
||||
buildPath: string,
|
||||
usedLibrariesList: Array<cc_arduino_cli_commands_v1_lib_pb.Library.AsObject>,
|
||||
executableSectionsSizeList: Array<ExecutableSectionSize.AsObject>,
|
||||
boardPlatform?: cc_arduino_cli_commands_v1_common_pb.PlatformReference.AsObject,
|
||||
buildPlatform?: cc_arduino_cli_commands_v1_common_pb.PlatformReference.AsObject,
|
||||
boardPlatform?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.AsObject,
|
||||
buildPlatform?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.AsObject,
|
||||
progress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,10 @@ proto.cc.arduino.cli.commands.v1.CompileRequest.toObject = function(includeInsta
|
||||
createCompilationDatabaseOnly: jspb.Message.getBooleanFieldWithDefault(msg, 21, false),
|
||||
sourceOverrideMap: (f = msg.getSourceOverrideMap()) ? f.toObject(includeInstance, undefined) : [],
|
||||
exportBinaries: (f = msg.getExportBinaries()) && google_protobuf_wrappers_pb.BoolValue.toObject(includeInstance, f),
|
||||
libraryList: (f = jspb.Message.getRepeatedField(msg, 24)) == null ? undefined : f
|
||||
libraryList: (f = jspb.Message.getRepeatedField(msg, 24)) == null ? undefined : f,
|
||||
keysKeychain: jspb.Message.getFieldWithDefault(msg, 25, ""),
|
||||
signKey: jspb.Message.getFieldWithDefault(msg, 26, ""),
|
||||
encryptKey: jspb.Message.getFieldWithDefault(msg, 27, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -271,6 +274,18 @@ proto.cc.arduino.cli.commands.v1.CompileRequest.deserializeBinaryFromReader = fu
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addLibrary(value);
|
||||
break;
|
||||
case 25:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setKeysKeychain(value);
|
||||
break;
|
||||
case 26:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSignKey(value);
|
||||
break;
|
||||
case 27:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setEncryptKey(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -446,6 +461,27 @@ proto.cc.arduino.cli.commands.v1.CompileRequest.serializeBinaryToWriter = functi
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getKeysKeychain();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
25,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getSignKey();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
26,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getEncryptKey();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
27,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -926,6 +962,60 @@ proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.clearLibraryList = fun
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string keys_keychain = 25;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.getKeysKeychain = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 25, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.setKeysKeychain = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 25, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string sign_key = 26;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.getSignKey = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 26, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.setSignKey = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 26, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string encrypt_key = 27;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.getEncryptKey = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 27, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.setEncryptKey = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 27, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
@@ -972,8 +1062,8 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.toObject = function(includeInst
|
||||
cc_arduino_cli_commands_v1_lib_pb.Library.toObject, includeInstance),
|
||||
executableSectionsSizeList: jspb.Message.toObjectList(msg.getExecutableSectionsSizeList(),
|
||||
proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.toObject, includeInstance),
|
||||
boardPlatform: (f = msg.getBoardPlatform()) && cc_arduino_cli_commands_v1_common_pb.PlatformReference.toObject(includeInstance, f),
|
||||
buildPlatform: (f = msg.getBuildPlatform()) && cc_arduino_cli_commands_v1_common_pb.PlatformReference.toObject(includeInstance, f),
|
||||
boardPlatform: (f = msg.getBoardPlatform()) && cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.toObject(includeInstance, f),
|
||||
buildPlatform: (f = msg.getBuildPlatform()) && cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.toObject(includeInstance, f),
|
||||
progress: (f = msg.getProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
@@ -1034,13 +1124,13 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.deserializeBinaryFromReader = f
|
||||
msg.addExecutableSectionsSize(value);
|
||||
break;
|
||||
case 6:
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.PlatformReference;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.PlatformReference.deserializeBinaryFromReader);
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.deserializeBinaryFromReader);
|
||||
msg.setBoardPlatform(value);
|
||||
break;
|
||||
case 7:
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.PlatformReference;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.PlatformReference.deserializeBinaryFromReader);
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.deserializeBinaryFromReader);
|
||||
msg.setBuildPlatform(value);
|
||||
break;
|
||||
case 8:
|
||||
@@ -1119,7 +1209,7 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.serializeBinaryToWriter = funct
|
||||
writer.writeMessage(
|
||||
6,
|
||||
f,
|
||||
cc_arduino_cli_commands_v1_common_pb.PlatformReference.serializeBinaryToWriter
|
||||
cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getBuildPlatform();
|
||||
@@ -1127,7 +1217,7 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.serializeBinaryToWriter = funct
|
||||
writer.writeMessage(
|
||||
7,
|
||||
f,
|
||||
cc_arduino_cli_commands_v1_common_pb.PlatformReference.serializeBinaryToWriter
|
||||
cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getProgress();
|
||||
@@ -1320,17 +1410,17 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearExecutableSectio
|
||||
|
||||
|
||||
/**
|
||||
* optional PlatformReference board_platform = 6;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.PlatformReference}
|
||||
* optional InstalledPlatformReference board_platform = 6;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getBoardPlatform = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.PlatformReference} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.PlatformReference, 6));
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference, 6));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.PlatformReference|undefined} value
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setBoardPlatform = function(value) {
|
||||
@@ -1357,17 +1447,17 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.hasBoardPlatform = fu
|
||||
|
||||
|
||||
/**
|
||||
* optional PlatformReference build_platform = 7;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.PlatformReference}
|
||||
* optional InstalledPlatformReference build_platform = 7;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getBuildPlatform = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.PlatformReference} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.PlatformReference, 7));
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference, 7));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.PlatformReference|undefined} value
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.InstalledPlatformReference|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setBuildPlatform = function(value) {
|
||||
|
||||
@@ -93,6 +93,9 @@ export class MonitorResponse extends jspb.Message {
|
||||
setAppliedSettingsList(value: Array<MonitorPortSetting>): MonitorResponse;
|
||||
addAppliedSettings(value?: MonitorPortSetting, index?: number): MonitorPortSetting;
|
||||
|
||||
getSuccess(): boolean;
|
||||
setSuccess(value: boolean): MonitorResponse;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): MonitorResponse.AsObject;
|
||||
@@ -109,6 +112,7 @@ export namespace MonitorResponse {
|
||||
error: string,
|
||||
rxData: Uint8Array | string,
|
||||
appliedSettingsList: Array<MonitorPortSetting.AsObject>,
|
||||
success: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -712,7 +712,8 @@ proto.cc.arduino.cli.commands.v1.MonitorResponse.toObject = function(includeInst
|
||||
error: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
rxData: msg.getRxData_asB64(),
|
||||
appliedSettingsList: jspb.Message.toObjectList(msg.getAppliedSettingsList(),
|
||||
proto.cc.arduino.cli.commands.v1.MonitorPortSetting.toObject, includeInstance)
|
||||
proto.cc.arduino.cli.commands.v1.MonitorPortSetting.toObject, includeInstance),
|
||||
success: jspb.Message.getBooleanFieldWithDefault(msg, 4, false)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -762,6 +763,10 @@ proto.cc.arduino.cli.commands.v1.MonitorResponse.deserializeBinaryFromReader = f
|
||||
reader.readMessage(value,proto.cc.arduino.cli.commands.v1.MonitorPortSetting.deserializeBinaryFromReader);
|
||||
msg.addAppliedSettings(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setSuccess(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -813,6 +818,13 @@ proto.cc.arduino.cli.commands.v1.MonitorResponse.serializeBinaryToWriter = funct
|
||||
proto.cc.arduino.cli.commands.v1.MonitorPortSetting.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getSuccess();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -914,6 +926,24 @@ proto.cc.arduino.cli.commands.v1.MonitorResponse.prototype.clearAppliedSettingsL
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bool success = 4;
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.MonitorResponse.prototype.getSuccess = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.MonitorResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.MonitorResponse.prototype.setSuccess = function(value) {
|
||||
return jspb.Message.setProto3BooleanField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -371,9 +371,6 @@ export class SupportedUserFieldsRequest extends jspb.Message {
|
||||
getProtocol(): string;
|
||||
setProtocol(value: string): SupportedUserFieldsRequest;
|
||||
|
||||
getAddress(): string;
|
||||
setAddress(value: string): SupportedUserFieldsRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): SupportedUserFieldsRequest.AsObject;
|
||||
@@ -390,7 +387,6 @@ export namespace SupportedUserFieldsRequest {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
fqbn: string,
|
||||
protocol: string,
|
||||
address: string,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2718,8 +2718,7 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.toObject = function(
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
fqbn: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
protocol: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
address: jspb.Message.getFieldWithDefault(msg, 4, "")
|
||||
protocol: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -2769,10 +2768,6 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.deserializeBinaryFro
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setProtocol(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setAddress(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -2824,13 +2819,6 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.serializeBinaryToWri
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getAddress();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2907,24 +2895,6 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.setProtoco
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string address = 4;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.getAddress = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.setAddress = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as temp from 'temp';
|
||||
import { promises as fs } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { promisify } from 'util';
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
@@ -28,9 +26,9 @@ import { DefaultCliConfig, CLI_CONFIG } from './cli-config';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { deepClone } from '@theia/core';
|
||||
import { duration } from '../common/decorators';
|
||||
|
||||
const deepmerge = require('deepmerge');
|
||||
const track = temp.track();
|
||||
|
||||
@injectable()
|
||||
export class ConfigServiceImpl
|
||||
@@ -54,18 +52,19 @@ export class ConfigServiceImpl
|
||||
protected ready = new Deferred<void>();
|
||||
protected readonly configChangeEmitter = new Emitter<Config>();
|
||||
|
||||
async onStart(): Promise<void> {
|
||||
await this.ensureCliConfigExists();
|
||||
this.cliConfig = await this.loadCliConfig();
|
||||
if (this.cliConfig) {
|
||||
const config = await this.mapCliConfigToAppConfig(this.cliConfig);
|
||||
if (config) {
|
||||
this.config = config;
|
||||
this.ready.resolve();
|
||||
return;
|
||||
onStart(): void {
|
||||
this.loadCliConfig().then(async (cliConfig) => {
|
||||
this.cliConfig = cliConfig;
|
||||
if (this.cliConfig) {
|
||||
const config = await this.mapCliConfigToAppConfig(this.cliConfig);
|
||||
if (config) {
|
||||
this.config = config;
|
||||
this.ready.resolve();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fireInvalidConfig();
|
||||
this.fireInvalidConfig();
|
||||
});
|
||||
}
|
||||
|
||||
async getCliConfigFileUri(): Promise<string> {
|
||||
@@ -75,8 +74,7 @@ export class ConfigServiceImpl
|
||||
|
||||
async getConfiguration(): Promise<Config> {
|
||||
await this.ready.promise;
|
||||
await this.daemon.ready;
|
||||
return { ...this.config, daemon: { port: await this.daemon.getPort() } };
|
||||
return { ...this.config };
|
||||
}
|
||||
|
||||
// Used by frontend to update the config.
|
||||
@@ -91,17 +89,10 @@ export class ConfigServiceImpl
|
||||
if (!copyDefaultCliConfig) {
|
||||
copyDefaultCliConfig = await this.getFallbackCliConfig();
|
||||
}
|
||||
const {
|
||||
additionalUrls,
|
||||
dataDirUri,
|
||||
downloadsDirUri,
|
||||
sketchDirUri,
|
||||
network,
|
||||
locale,
|
||||
} = config;
|
||||
const { additionalUrls, dataDirUri, sketchDirUri, network, locale } =
|
||||
config;
|
||||
copyDefaultCliConfig.directories = {
|
||||
data: FileUri.fsPath(dataDirUri),
|
||||
downloads: FileUri.fsPath(downloadsDirUri),
|
||||
user: FileUri.fsPath(sketchDirUri),
|
||||
};
|
||||
copyDefaultCliConfig.board_manager = {
|
||||
@@ -135,76 +126,44 @@ export class ConfigServiceImpl
|
||||
return this.daemon.getVersion();
|
||||
}
|
||||
|
||||
async isInDataDir(uri: string): Promise<boolean> {
|
||||
return this.getConfiguration().then(({ dataDirUri }) =>
|
||||
new URI(dataDirUri).isEqualOrParent(new URI(uri))
|
||||
);
|
||||
}
|
||||
|
||||
async isInSketchDir(uri: string): Promise<boolean> {
|
||||
return this.getConfiguration().then(({ sketchDirUri }) =>
|
||||
new URI(sketchDirUri).isEqualOrParent(new URI(uri))
|
||||
);
|
||||
}
|
||||
|
||||
protected async loadCliConfig(): Promise<DefaultCliConfig | undefined> {
|
||||
@duration()
|
||||
protected async loadCliConfig(
|
||||
initializeIfAbsent = true
|
||||
): Promise<DefaultCliConfig | undefined> {
|
||||
const cliConfigFileUri = await this.getCliConfigFileUri();
|
||||
const cliConfigPath = FileUri.fsPath(cliConfigFileUri);
|
||||
try {
|
||||
const content = await promisify(fs.readFile)(cliConfigPath, {
|
||||
const content = await fs.readFile(cliConfigPath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const model = yaml.safeLoad(content) || {};
|
||||
// The CLI can run with partial (missing `port`, `directories`), the app cannot, we merge the default with the user's config.
|
||||
const model = (yaml.safeLoad(content) || {}) as DefaultCliConfig;
|
||||
if (model.directories.data && model.directories.user) {
|
||||
return model;
|
||||
}
|
||||
// The CLI can run with partial (missing `port`, `directories`), the IDE2 cannot.
|
||||
// We merge the default CLI config with the partial user's config.
|
||||
const fallbackModel = await this.getFallbackCliConfig();
|
||||
return deepmerge(fallbackModel, model) as DefaultCliConfig;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error occurred when loading CLI config from ${cliConfigPath}.`,
|
||||
error
|
||||
);
|
||||
if ('code' in error && error.code === 'ENOENT') {
|
||||
if (initializeIfAbsent) {
|
||||
await this.initCliConfigTo(dirname(cliConfigPath));
|
||||
return this.loadCliConfig(false);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected async getFallbackCliConfig(): Promise<DefaultCliConfig> {
|
||||
const cliPath = await this.daemon.getExecPath();
|
||||
const throwawayDirPath = await new Promise<string>((resolve, reject) => {
|
||||
track.mkdir({}, (err, dirPath) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(dirPath);
|
||||
});
|
||||
});
|
||||
await spawnCommand(`"${cliPath}"`, [
|
||||
const rawJson = await spawnCommand(`"${cliPath}"`, [
|
||||
'config',
|
||||
'init',
|
||||
'--dest-dir',
|
||||
`"${throwawayDirPath}"`,
|
||||
'dump',
|
||||
'format',
|
||||
'--json',
|
||||
]);
|
||||
const rawYaml = await promisify(fs.readFile)(
|
||||
path.join(throwawayDirPath, CLI_CONFIG),
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
const model = yaml.safeLoad(rawYaml.trim());
|
||||
return model as DefaultCliConfig;
|
||||
}
|
||||
|
||||
protected async ensureCliConfigExists(): Promise<void> {
|
||||
const cliConfigFileUri = await this.getCliConfigFileUri();
|
||||
const cliConfigPath = FileUri.fsPath(cliConfigFileUri);
|
||||
let exists = await promisify(fs.exists)(cliConfigPath);
|
||||
if (!exists) {
|
||||
await this.initCliConfigTo(path.dirname(cliConfigPath));
|
||||
exists = await promisify(fs.exists)(cliConfigPath);
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
`Could not initialize the default CLI configuration file at ${cliConfigPath}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
return JSON.parse(rawJson);
|
||||
}
|
||||
|
||||
protected async initCliConfigTo(fsPathToDir: string): Promise<void> {
|
||||
@@ -220,8 +179,8 @@ export class ConfigServiceImpl
|
||||
protected async mapCliConfigToAppConfig(
|
||||
cliConfig: DefaultCliConfig
|
||||
): Promise<Config> {
|
||||
const { directories, locale = 'en', daemon } = cliConfig;
|
||||
const { data, user, downloads } = directories;
|
||||
const { directories, locale = 'en' } = cliConfig;
|
||||
const { user, data } = directories;
|
||||
const additionalUrls: Array<string> = [];
|
||||
if (cliConfig.board_manager && cliConfig.board_manager.additional_urls) {
|
||||
additionalUrls.push(
|
||||
@@ -232,11 +191,9 @@ export class ConfigServiceImpl
|
||||
return {
|
||||
dataDirUri: FileUri.create(data).toString(),
|
||||
sketchDirUri: FileUri.create(user).toString(),
|
||||
downloadsDirUri: FileUri.create(downloads).toString(),
|
||||
additionalUrls,
|
||||
network,
|
||||
locale,
|
||||
daemon,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { GrpcClientProvider } from './grpc-client-provider';
|
||||
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
|
||||
@@ -16,7 +20,8 @@ import {
|
||||
} from './cli-protocol/cc/arduino/cli/commands/v1/commands_pb';
|
||||
import * as commandsGrpcPb from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
|
||||
import { NotificationServiceServer } from '../common/protocol';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Deferred, retry } from '@theia/core/lib/common/promise-util';
|
||||
import { Status as RpcStatus } from './cli-protocol/google/rpc/status_pb';
|
||||
|
||||
@injectable()
|
||||
export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Client> {
|
||||
@@ -48,9 +53,7 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
this._initialized = new Deferred<void>();
|
||||
}
|
||||
|
||||
protected async reconcileClient(): Promise<void> {
|
||||
const port = await this.daemon.getPort();
|
||||
|
||||
protected override async reconcileClient(port: string): Promise<void> {
|
||||
if (port && port === this._port) {
|
||||
// No need to create a new gRPC client, but we have to update the indexes.
|
||||
if (this._client && !(this._client instanceof Error)) {
|
||||
@@ -58,29 +61,47 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
this.onClientReadyEmitter.fire();
|
||||
}
|
||||
} else {
|
||||
await super.reconcileClient();
|
||||
await super.reconcileClient(port);
|
||||
this.onClientReadyEmitter.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
this.daemon.ready.then(async () => {
|
||||
protected override init(): void {
|
||||
this.daemon.getPort().then(async (port) => {
|
||||
// First create the client and the instance synchronously
|
||||
// and notify client is ready.
|
||||
// TODO: Creation failure should probably be handled here
|
||||
await this.reconcileClient().then(() => {
|
||||
this._created.resolve();
|
||||
});
|
||||
await this.reconcileClient(port); // create instance
|
||||
this._created.resolve();
|
||||
|
||||
// If client has been created correctly update indexes and initialize
|
||||
// its instance by loading platforms and cores.
|
||||
// Normal startup workflow:
|
||||
// 1. create instance,
|
||||
// 2. init instance,
|
||||
// 3. update indexes asynchronously.
|
||||
|
||||
// First startup workflow:
|
||||
// 1. create instance,
|
||||
// 2. update indexes and wait,
|
||||
// 3. init instance.
|
||||
if (this._client && !(this._client instanceof Error)) {
|
||||
await this.updateIndexes(this._client)
|
||||
.then(this.initInstance)
|
||||
.then(() => {
|
||||
try {
|
||||
await this.initInstance(this._client); // init instance
|
||||
this._initialized.resolve();
|
||||
this.updateIndex(this._client); // Update the indexes asynchronously
|
||||
} catch (error: unknown) {
|
||||
if (
|
||||
this.isPackageIndexMissingError(error) ||
|
||||
this.isDiscoveryNotFoundError(error)
|
||||
) {
|
||||
// If it's a first start, IDE2 must run index update before the init request.
|
||||
await this.updateIndexes(this._client);
|
||||
await this.initInstance(this._client);
|
||||
this._initialized.resolve();
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -93,6 +114,41 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
});
|
||||
}
|
||||
|
||||
private isPackageIndexMissingError(error: unknown): boolean {
|
||||
const assert = (message: string) =>
|
||||
message.includes('loading json index file');
|
||||
// https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247
|
||||
return this.isRpcStatusError(error, assert);
|
||||
}
|
||||
|
||||
private isDiscoveryNotFoundError(error: unknown): boolean {
|
||||
const assert = (message: string) =>
|
||||
message.includes('discovery') &&
|
||||
(message.includes('not found') || message.includes('not installed'));
|
||||
// https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L740
|
||||
// https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L744
|
||||
return this.isRpcStatusError(error, assert);
|
||||
}
|
||||
|
||||
private isCancelError(error: unknown): boolean {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
error.message.toLocaleLowerCase().includes('cancelled on client')
|
||||
);
|
||||
}
|
||||
|
||||
// Final error codes are not yet defined by the CLI. Hence, we do string matching in the message RPC status.
|
||||
private isRpcStatusError(
|
||||
error: unknown,
|
||||
assert: (message: string) => boolean
|
||||
) {
|
||||
if (error instanceof RpcStatus) {
|
||||
const { message } = RpcStatus.toObject(false, error);
|
||||
return assert(message.toLocaleLowerCase());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async createClient(
|
||||
port: string | number
|
||||
): Promise<CoreClientProvider.Client> {
|
||||
@@ -134,8 +190,9 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
}: CoreClientProvider.Client): Promise<void> {
|
||||
const initReq = new InitRequest();
|
||||
initReq.setInstance(instance);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const stream = client.init(initReq);
|
||||
const errorStatus: RpcStatus[] = [];
|
||||
stream.on('data', (res: InitResponse) => {
|
||||
const progress = res.getInitProgress();
|
||||
if (progress) {
|
||||
@@ -151,55 +208,39 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
}
|
||||
}
|
||||
|
||||
const err = res.getError();
|
||||
if (err) {
|
||||
console.error(err.getMessage());
|
||||
const error = res.getError();
|
||||
if (error) {
|
||||
console.error(error.getMessage());
|
||||
errorStatus.push(error);
|
||||
// Cancel the init request. No need to wait until the end of the event. The init has already failed.
|
||||
// Canceling the request will result in a cancel error, but we need to reject with the original error later.
|
||||
stream.cancel();
|
||||
}
|
||||
});
|
||||
stream.on('error', (err) => reject(err));
|
||||
stream.on('end', resolve);
|
||||
stream.on('error', (error) => {
|
||||
// On any error during the init request, the request is canceled.
|
||||
// On cancel, the IDE2 ignores the cancel error and rejects with the original one.
|
||||
reject(
|
||||
this.isCancelError(error) && errorStatus.length
|
||||
? errorStatus[0]
|
||||
: error
|
||||
);
|
||||
});
|
||||
stream.on('end', () =>
|
||||
errorStatus.length ? reject(errorStatus) : resolve()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
protected async updateIndexes({
|
||||
client,
|
||||
instance,
|
||||
}: CoreClientProvider.Client): Promise<CoreClientProvider.Client> {
|
||||
// in a separate promise, try and update the index
|
||||
let indexUpdateSucceeded = true;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
await this.updateIndex({ client, instance });
|
||||
indexUpdateSucceeded = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
console.error(`Error while updating index in attempt ${i}.`, e);
|
||||
}
|
||||
}
|
||||
if (!indexUpdateSucceeded) {
|
||||
console.error('Could not update the index. Please restart to try again.');
|
||||
}
|
||||
|
||||
let libIndexUpdateSucceeded = true;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
await this.updateLibraryIndex({ client, instance });
|
||||
libIndexUpdateSucceeded = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
console.error(`Error while updating library index in attempt ${i}.`, e);
|
||||
}
|
||||
}
|
||||
if (!libIndexUpdateSucceeded) {
|
||||
console.error(
|
||||
'Could not update the library index. Please restart to try again.'
|
||||
);
|
||||
}
|
||||
|
||||
if (indexUpdateSucceeded && libIndexUpdateSucceeded) {
|
||||
this.notificationService.notifyIndexUpdated();
|
||||
}
|
||||
return { client, instance };
|
||||
protected async updateIndexes(
|
||||
client: CoreClientProvider.Client
|
||||
): Promise<CoreClientProvider.Client> {
|
||||
await Promise.all([
|
||||
retry(() => this.updateIndex(client), 50, 3),
|
||||
retry(() => this.updateLibraryIndex(client), 50, 3),
|
||||
]);
|
||||
this.notificationService.notifyIndexUpdated();
|
||||
return client;
|
||||
}
|
||||
|
||||
protected async updateLibraryIndex({
|
||||
@@ -231,7 +272,9 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
resp.on('error', reject);
|
||||
resp.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
resp.on('end', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import { ILogger, LogLevel } from '@theia/core/lib/common/logger';
|
||||
|
||||
export interface DaemonLog {
|
||||
readonly time: string;
|
||||
readonly level: DaemonLog.Level;
|
||||
readonly msg: string;
|
||||
}
|
||||
|
||||
export namespace DaemonLog {
|
||||
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 System {
|
||||
readonly os: string;
|
||||
// readonly Resource: Resource;
|
||||
}
|
||||
|
||||
export namespace System {
|
||||
export function toString(system: System): string {
|
||||
return `OS: ${system.os}`;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return (
|
||||
!!arg &&
|
||||
typeof arg.time === 'string' &&
|
||||
typeof arg.level === 'string' &&
|
||||
typeof arg.msg === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
export function toLogLevel(log: DaemonLog): LogLevel {
|
||||
const { level } = log;
|
||||
switch (level) {
|
||||
case 'trace':
|
||||
return LogLevel.TRACE;
|
||||
case 'debug':
|
||||
return LogLevel.DEBUG;
|
||||
case 'info':
|
||||
return LogLevel.INFO;
|
||||
case 'warning':
|
||||
return LogLevel.WARN;
|
||||
case 'error':
|
||||
return LogLevel.ERROR;
|
||||
default:
|
||||
return LogLevel.INFO;
|
||||
}
|
||||
}
|
||||
|
||||
export function log(logger: ILogger, logMessages: string): void {
|
||||
const parsed = parse(logMessages);
|
||||
for (const log of parsed) {
|
||||
const logLevel = toLogLevel(log);
|
||||
const message = toMessage(log, { omitLogLevel: true });
|
||||
logger.log(logLevel, message);
|
||||
}
|
||||
}
|
||||
|
||||
function parse(toLog: string): DaemonLog[] {
|
||||
const messages = toLog.trim().split('\n');
|
||||
const result: DaemonLog[] = [];
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
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: messages[i],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toPrettyString(logMessages: string): string {
|
||||
const parsed = parse(logMessages);
|
||||
return parsed.map((log) => toMessage(log)).join('\n') + '\n';
|
||||
}
|
||||
|
||||
function toMessage(
|
||||
log: DaemonLog,
|
||||
options: { omitLogLevel: boolean } = { omitLogLevel: false }
|
||||
): string {
|
||||
const details = Object.keys(log)
|
||||
.filter((key) => key !== 'msg' && key !== 'level' && key !== 'time')
|
||||
.map((key) => toDetails(log, key))
|
||||
.join(', ');
|
||||
const logLevel = options.omitLogLevel
|
||||
? ''
|
||||
: `[${log.level.toUpperCase()}] `;
|
||||
return `${logLevel}${log.msg}${!!details ? ` [${details}]` : ''}`;
|
||||
}
|
||||
|
||||
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:'); // Remove the quotes from the property keys.
|
||||
}
|
||||
return `${key.toLowerCase()}: ${value}`;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { join, basename } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { Sketch, SketchContainer } from '../common/protocol/sketches-service';
|
||||
import {
|
||||
Sketch,
|
||||
SketchRef,
|
||||
SketchContainer,
|
||||
} from '../common/protocol/sketches-service';
|
||||
import { SketchesServiceImpl } from './sketches-service-impl';
|
||||
import { ExamplesService } from '../common/protocol/examples-service';
|
||||
import {
|
||||
@@ -13,6 +20,71 @@ import {
|
||||
LibraryService,
|
||||
} from '../common/protocol';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
import { duration } from '../common/decorators';
|
||||
import { URI } from '@theia/core/lib/common/uri';
|
||||
import { Path } from '@theia/core/lib/common/path';
|
||||
|
||||
interface BuiltInSketchRef {
|
||||
readonly name: string;
|
||||
readonly relativePath: string;
|
||||
}
|
||||
namespace BuiltInSketchRef {
|
||||
export function toSketchRef(
|
||||
{ name, relativePath }: BuiltInSketchRef,
|
||||
root: URI
|
||||
): SketchRef {
|
||||
return {
|
||||
name,
|
||||
uri: root.resolve(relativePath).toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
interface BuiltInSketchContainer {
|
||||
readonly label: string;
|
||||
readonly children: BuiltInSketchContainer[];
|
||||
readonly sketches: BuiltInSketchRef[];
|
||||
}
|
||||
namespace BuiltInSketchContainer {
|
||||
export function toSketchContainer(
|
||||
source: BuiltInSketchContainer,
|
||||
root: URI
|
||||
): SketchContainer {
|
||||
return {
|
||||
label: source.label,
|
||||
children: source.children.map((child) => toSketchContainer(child, root)),
|
||||
sketches: source.sketches.map((child) =>
|
||||
BuiltInSketchRef.toSketchRef(child, root)
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class BuiltInExamplesServiceImpl {
|
||||
protected _builtIns: SketchContainer[] | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.builtIns();
|
||||
}
|
||||
|
||||
async builtIns(): Promise<SketchContainer[]> {
|
||||
if (this._builtIns) {
|
||||
return this._builtIns;
|
||||
}
|
||||
const examplesRootPath = join(__dirname, '..', '..', 'Examples');
|
||||
const examplesRootUri = FileUri.create(examplesRootPath);
|
||||
const rawJson = await fs.promises.readFile(
|
||||
join(examplesRootPath, 'examples.json'),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
const examples: BuiltInSketchContainer[] = JSON.parse(rawJson);
|
||||
this._builtIns = examples.map((container) =>
|
||||
BuiltInSketchContainer.toSketchContainer(container, examplesRootUri)
|
||||
);
|
||||
return this._builtIns;
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ExamplesServiceImpl implements ExamplesService {
|
||||
@@ -25,28 +97,14 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
|
||||
protected _all: SketchContainer[] | undefined;
|
||||
@inject(BuiltInExamplesServiceImpl)
|
||||
private readonly builtInExamplesService: BuiltInExamplesServiceImpl;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.builtIns();
|
||||
builtIns(): Promise<SketchContainer[]> {
|
||||
return this.builtInExamplesService.builtIns();
|
||||
}
|
||||
|
||||
async builtIns(): Promise<SketchContainer[]> {
|
||||
if (this._all) {
|
||||
return this._all;
|
||||
}
|
||||
const exampleRootPath = join(__dirname, '..', '..', 'Examples');
|
||||
const exampleNames = await promisify(fs.readdir)(exampleRootPath);
|
||||
this._all = await Promise.all(
|
||||
exampleNames
|
||||
.map((name) => join(exampleRootPath, name))
|
||||
.map((path) => this.load(path))
|
||||
);
|
||||
return this._all;
|
||||
}
|
||||
|
||||
// TODO: decide whether it makes sense to cache them. Keys should be: `fqbn` + version of containing core/library.
|
||||
@duration()
|
||||
async installed({ fqbn }: { fqbn?: string }): Promise<{
|
||||
user: SketchContainer[];
|
||||
current: SketchContainer[];
|
||||
@@ -59,7 +117,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
fqbn,
|
||||
});
|
||||
for (const pkg of packages) {
|
||||
const container = await this.tryGroupExamples(pkg);
|
||||
const container = await this.tryGroupExamplesNew(pkg);
|
||||
const { location } = pkg;
|
||||
if (location === LibraryLocation.USER) {
|
||||
user.push(container);
|
||||
@@ -72,6 +130,9 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
any.push(container);
|
||||
}
|
||||
}
|
||||
// user.sort((left, right) => left.label.localeCompare(right.label));
|
||||
// current.sort((left, right) => left.label.localeCompare(right.label));
|
||||
// any.sort((left, right) => left.label.localeCompare(right.label));
|
||||
return { user, current, any };
|
||||
}
|
||||
|
||||
@@ -80,60 +141,101 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
* folder hierarchy. This method tries to workaround it by falling back to the `installDirUri` and manually creating the
|
||||
* location of the examples. Otherwise it creates the example container from the direct examples FS paths.
|
||||
*/
|
||||
protected async tryGroupExamples({
|
||||
protected async tryGroupExamplesNew({
|
||||
label,
|
||||
exampleUris,
|
||||
installDirUri,
|
||||
}: LibraryPackage): Promise<SketchContainer> {
|
||||
const paths = exampleUris.map((uri) => FileUri.fsPath(uri));
|
||||
if (installDirUri) {
|
||||
for (const example of [
|
||||
'example',
|
||||
'Example',
|
||||
'EXAMPLE',
|
||||
'examples',
|
||||
'Examples',
|
||||
'EXAMPLES',
|
||||
]) {
|
||||
const examplesPath = join(FileUri.fsPath(installDirUri), example);
|
||||
const exists = await promisify(fs.exists)(examplesPath);
|
||||
const isDir =
|
||||
exists && (await promisify(fs.lstat)(examplesPath)).isDirectory();
|
||||
if (isDir) {
|
||||
const fileNames = await promisify(fs.readdir)(examplesPath);
|
||||
const children: SketchContainer[] = [];
|
||||
const sketches: Sketch[] = [];
|
||||
for (const fileName of fileNames) {
|
||||
const subPath = join(examplesPath, fileName);
|
||||
const subIsDir = (await promisify(fs.lstat)(subPath)).isDirectory();
|
||||
if (subIsDir) {
|
||||
const sketch = await this.tryLoadSketch(subPath);
|
||||
if (!sketch) {
|
||||
const container = await this.load(subPath);
|
||||
if (container.children.length || container.sketches.length) {
|
||||
children.push(container);
|
||||
}
|
||||
} else {
|
||||
sketches.push(sketch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
label,
|
||||
children,
|
||||
sketches,
|
||||
};
|
||||
}
|
||||
}
|
||||
const container = SketchContainer.create(label);
|
||||
if (!installDirUri || !exampleUris.length) {
|
||||
return container;
|
||||
}
|
||||
const sketches = await Promise.all(
|
||||
paths.map((path) => this.tryLoadSketch(path))
|
||||
);
|
||||
return {
|
||||
label,
|
||||
children: [],
|
||||
sketches: sketches.filter(notEmpty),
|
||||
};
|
||||
// Args example:
|
||||
// exampleUris
|
||||
// 0:'file:///Users/a.kitta/Documents/Arduino/libraries/ATOM_DTU_CAT1/examples/MQTT'
|
||||
// 1:'file:///Users/a.kitta/Documents/Arduino/libraries/ATOM_DTU_CAT1/examples/Modbus/ModBus-RTU/Master'
|
||||
// 2:'file:///Users/a.kitta/Documents/Arduino/libraries/ATOM_DTU_CAT1/examples/Modbus/ModBus-RTU/Slave'
|
||||
// installDirUri
|
||||
// 'file:///Users/a.kitta/Documents/Arduino/libraries/ATOM_DTU_CAT1'
|
||||
// Expected menu structure:
|
||||
// ATOM_DTU_CAT1 > Modbus > ModBus-RTU > Master
|
||||
// | > Slave
|
||||
// > MQTT
|
||||
const logInfo = (ref: SketchRef) =>
|
||||
`Example URI: ${ref.uri}, install location URI: ${installDirUri}.`;
|
||||
for (const ref of exampleUris.map(SketchRef.fromUri)) {
|
||||
const path = new URI(installDirUri).relative(new URI(ref.uri));
|
||||
if (!path) {
|
||||
console.warn(
|
||||
`Could not resolve the sketch location from its install location. Skipping. ${logInfo(
|
||||
ref
|
||||
)}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (path.isAbsolute) {
|
||||
console.warn(
|
||||
`Expected a relative path between the sketch and the install locations. Skipping. Path was: ${path}. ${logInfo(
|
||||
ref
|
||||
)}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const pathSegments = path.toString().split(Path.separator);
|
||||
if (pathSegments.length < 2) {
|
||||
console.warn(
|
||||
`Expected at least two segments long relative path. Skipping. Path segments were: ${pathSegments}. ${logInfo(
|
||||
ref
|
||||
)}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// the relative must start start with `example` or `Examples` or `EXAMPLE`, .etc. It's open source.
|
||||
if (!/^examples?$/gi.test(pathSegments[0])) {
|
||||
console.warn(
|
||||
`First segment must start with "examples-like". More formally: \`/^examples?$/gi\`. Path segments were: ${pathSegments}. ${logInfo(
|
||||
ref
|
||||
)}`
|
||||
);
|
||||
}
|
||||
const getOrCreateChildContainer = (
|
||||
label: string,
|
||||
parent: SketchContainer
|
||||
) => {
|
||||
let child = parent.children.find(
|
||||
({ label: childLabel }) => childLabel === label
|
||||
);
|
||||
if (!child) {
|
||||
child = SketchContainer.create(label);
|
||||
parent.children.push(child);
|
||||
//TODO: remove or move sort
|
||||
parent.children.sort((left, right) =>
|
||||
left.label.localeCompare(right.label)
|
||||
);
|
||||
}
|
||||
return child;
|
||||
};
|
||||
const refContainer = pathSegments.reduce(
|
||||
(container, segment, index, segments) => {
|
||||
if (index === 0) {
|
||||
// skip the first "example-like" segment
|
||||
return container;
|
||||
}
|
||||
if (index === segments.length - 1) {
|
||||
// if last segment, it's the example sketch itself, do not create container for it.
|
||||
return container;
|
||||
}
|
||||
return getOrCreateChildContainer(segment, container);
|
||||
},
|
||||
container
|
||||
);
|
||||
refContainer.sketches.push(ref);
|
||||
//TODO: remove or move sort
|
||||
refContainer.sketches.sort((left, right) =>
|
||||
left.name.localeCompare(right.name)
|
||||
);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
// Built-ins are included inside the IDE.
|
||||
@@ -146,17 +248,19 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
throw new Error(`${path} is not a directory.`);
|
||||
}
|
||||
const names = await promisify(fs.readdir)(path);
|
||||
const sketches: Sketch[] = [];
|
||||
const sketches: SketchRef[] = [];
|
||||
const children: SketchContainer[] = [];
|
||||
for (const p of names.map((name) => join(path, name))) {
|
||||
const stat = await promisify(fs.stat)(p);
|
||||
if (stat.isDirectory()) {
|
||||
const sketch = await this.tryLoadSketch(p);
|
||||
if (sketch) {
|
||||
sketches.push(sketch);
|
||||
sketches.push({ name: sketch.name, uri: sketch.uri });
|
||||
sketches.sort((left, right) => left.name.localeCompare(right.name));
|
||||
} else {
|
||||
const child = await this.load(p);
|
||||
children.push(child);
|
||||
children.sort((left, right) => left.label.localeCompare(right.label));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
@@ -15,16 +19,18 @@ export abstract class GrpcClientProvider<C> {
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
|
||||
protected _port: string | number | undefined;
|
||||
protected _port: string | undefined;
|
||||
protected _client: C | Error | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
const updateClient = () => {
|
||||
this.reconcileClient();
|
||||
};
|
||||
this.configService.onConfigChange(updateClient);
|
||||
this.daemon.ready.then(updateClient);
|
||||
this.configService.onConfigChange(() => {
|
||||
// Only reconcile the gRPC client if the port is known. Hence the CLI daemon is running.
|
||||
if (this._port) {
|
||||
this.reconcileClient(this._port);
|
||||
}
|
||||
});
|
||||
this.daemon.getPort().then((port) => this.reconcileClient(port));
|
||||
this.daemon.onDaemonStopped(() => {
|
||||
if (this._client && !(this._client instanceof Error)) {
|
||||
this.close(this._client);
|
||||
@@ -36,32 +42,25 @@ export abstract class GrpcClientProvider<C> {
|
||||
|
||||
async client(): Promise<C | Error | undefined> {
|
||||
try {
|
||||
await this.daemon.ready;
|
||||
await this.daemon.getPort();
|
||||
return this._client;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
protected async reconcileClient(): Promise<void> {
|
||||
const port = await this.daemon.getPort();
|
||||
|
||||
if (this._port === port) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
protected async reconcileClient(port: string): Promise<void> {
|
||||
this._port = port;
|
||||
if (this._client && !(this._client instanceof Error)) {
|
||||
this.close(this._client);
|
||||
this._client = undefined;
|
||||
}
|
||||
if (this._port) {
|
||||
try {
|
||||
const client = await this.createClient(this._port);
|
||||
this._client = client;
|
||||
} catch (error) {
|
||||
this.logger.error('Could not create client for gRPC.', error);
|
||||
this._client = error;
|
||||
}
|
||||
try {
|
||||
const client = await this.createClient(this._port);
|
||||
this._client = client;
|
||||
} catch (error) {
|
||||
this.logger.error('Could not create client for gRPC.', error);
|
||||
this._client = error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ export class NotificationServiceServerImpl
|
||||
this.clients.forEach((client) => client.notifyIndexUpdated());
|
||||
}
|
||||
|
||||
notifyDaemonStarted(): void {
|
||||
this.clients.forEach((client) => client.notifyDaemonStarted());
|
||||
notifyDaemonStarted(port: string): void {
|
||||
this.clients.forEach((client) => client.notifyDaemonStarted(port));
|
||||
}
|
||||
|
||||
notifyDaemonStopped(): void {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import * as minimatch from 'minimatch';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as temp from 'temp';
|
||||
import * as tempDir from 'temp-dir';
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import { ncp } from 'ncp';
|
||||
import { promisify } from 'util';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import { isWindows, isOSX } from '@theia/core/lib/common/os';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import {
|
||||
SketchesService,
|
||||
Sketch,
|
||||
SketchRef,
|
||||
SketchContainer,
|
||||
} from '../common/protocol/sketches-service';
|
||||
import { firstToLowerCase } from '../common/utils';
|
||||
@@ -24,16 +25,28 @@ import {
|
||||
ArchiveSketchRequest,
|
||||
LoadSketchRequest,
|
||||
} from './cli-protocol/cc/arduino/cli/commands/v1/commands_pb';
|
||||
import { duration } from '../common/decorators';
|
||||
import * as glob from 'glob';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
|
||||
const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
||||
|
||||
const prefix = '.arduinoIDE-unsaved';
|
||||
|
||||
@injectable()
|
||||
export class SketchesServiceImpl extends CoreClientAware
|
||||
implements SketchesService {
|
||||
export class SketchesServiceImpl
|
||||
extends CoreClientAware
|
||||
implements SketchesService
|
||||
{
|
||||
private sketchSuffixIndex = 1;
|
||||
private lastSketchBaseName: string;
|
||||
// If on macOS, the `temp-dir` lib will make sure there is resolved realpath.
|
||||
// If on Windows, the `C:\Users\KITTAA~1\AppData\Local\Temp` path will be resolved and normalized to `C:\Users\kittaakos\AppData\Local\Temp`.
|
||||
// Note: VS Code URI normalizes the drive letter. `C:` will be converted into `c:`.
|
||||
// https://github.com/Microsoft/vscode/issues/68325#issuecomment-462239992
|
||||
private tempDirRealpath = isOSX
|
||||
? tempDir
|
||||
: maybeNormalizeDrive(fs.realpathSync.native(tempDir));
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@@ -43,116 +56,172 @@ export class SketchesServiceImpl extends CoreClientAware
|
||||
|
||||
@inject(EnvVariablesServer)
|
||||
protected readonly envVariableServer: EnvVariablesServer;
|
||||
|
||||
async getSketches({
|
||||
uri,
|
||||
exclude,
|
||||
}: {
|
||||
uri?: string;
|
||||
exclude?: string[];
|
||||
}): Promise<SketchContainerWithDetails> {
|
||||
const start = Date.now();
|
||||
let sketchbookPath: undefined | string;
|
||||
if (!uri) {
|
||||
const { sketchDirUri } = await this.configService.getConfiguration();
|
||||
sketchbookPath = FileUri.fsPath(sketchDirUri);
|
||||
if (!(await promisify(fs.exists)(sketchbookPath))) {
|
||||
await promisify(fs.mkdir)(sketchbookPath, { recursive: true });
|
||||
}
|
||||
} else {
|
||||
sketchbookPath = FileUri.fsPath(uri);
|
||||
}
|
||||
const container: SketchContainerWithDetails = {
|
||||
label: uri ? path.basename(sketchbookPath) : 'Sketchbook',
|
||||
sketches: [],
|
||||
children: [],
|
||||
};
|
||||
if (!(await promisify(fs.exists)(sketchbookPath))) {
|
||||
return container;
|
||||
}
|
||||
const stat = await promisify(fs.stat)(sketchbookPath);
|
||||
if (!stat.isDirectory()) {
|
||||
return container;
|
||||
}
|
||||
}): Promise<SketchContainer> {
|
||||
const [/*old,*/ _new] = await Promise.all([
|
||||
// this.getSketchesOld({ uri, exclude }),
|
||||
this.getSketchesNew({ uri, exclude }),
|
||||
]);
|
||||
return _new;
|
||||
}
|
||||
|
||||
const recursivelyLoad = async (
|
||||
fsPath: string,
|
||||
containerToLoad: SketchContainerWithDetails
|
||||
) => {
|
||||
const filenames = await promisify(fs.readdir)(fsPath);
|
||||
for (const name of filenames) {
|
||||
const childFsPath = path.join(fsPath, name);
|
||||
let skip = false;
|
||||
for (const pattern of exclude || [
|
||||
'**/libraries/**',
|
||||
'**/hardware/**',
|
||||
]) {
|
||||
if (!skip && minimatch(childFsPath, pattern)) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const stat = await promisify(fs.stat)(childFsPath);
|
||||
if (stat.isDirectory()) {
|
||||
const sketch = await this._isSketchFolder(
|
||||
FileUri.create(childFsPath).toString()
|
||||
);
|
||||
if (sketch) {
|
||||
containerToLoad.sketches.push({
|
||||
...sketch,
|
||||
mtimeMs: stat.mtimeMs,
|
||||
});
|
||||
@duration()
|
||||
async getSketchesNew({
|
||||
uri,
|
||||
exclude,
|
||||
}: {
|
||||
uri?: string;
|
||||
exclude?: string[];
|
||||
}): Promise<SketchContainer> {
|
||||
const root = await this.root(uri);
|
||||
const pathToAllSketchFiles = await new Promise<string[]>(
|
||||
(resolve, reject) => {
|
||||
glob(
|
||||
'/!(libraries|hardware)/**/*.{ino,pde}',
|
||||
{ root },
|
||||
(error, results) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
const childContainer: SketchContainerWithDetails = {
|
||||
label: name,
|
||||
children: [],
|
||||
sketches: [],
|
||||
};
|
||||
await recursivelyLoad(childFsPath, childContainer);
|
||||
if (!SketchContainer.isEmpty(childContainer)) {
|
||||
containerToLoad.children.push(childContainer);
|
||||
}
|
||||
resolve(results);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.warn(`Could not load sketch from ${childFsPath}.`);
|
||||
}
|
||||
);
|
||||
}
|
||||
containerToLoad.sketches.sort(
|
||||
(left, right) => right.mtimeMs - left.mtimeMs
|
||||
);
|
||||
return containerToLoad;
|
||||
};
|
||||
|
||||
await recursivelyLoad(sketchbookPath, container);
|
||||
SketchContainer.prune(container);
|
||||
console.debug(
|
||||
`Loading the sketches from ${sketchbookPath} took ${
|
||||
Date.now() - start
|
||||
} ms.`
|
||||
);
|
||||
// Sort by path length to filter out nested sketches, such as the `Nested_folder` inside the `Folder` sketch.
|
||||
//
|
||||
// `directories#user`
|
||||
// |
|
||||
// +--Folder
|
||||
// |
|
||||
// +--Folder.ino
|
||||
// |
|
||||
// +--Nested_folder
|
||||
// |
|
||||
// +--Nested_folder.ino
|
||||
pathToAllSketchFiles.sort((left, right) => left.length - right.length);
|
||||
const container = SketchContainer.create(
|
||||
uri ? path.basename(root) : 'Sketchbook'
|
||||
);
|
||||
const getOrCreateChildContainer = (
|
||||
parent: SketchContainer,
|
||||
segments: string[]
|
||||
) => {
|
||||
if (segments.length === 1) {
|
||||
throw new Error(
|
||||
`Expected at least two segments relative path: ['ExampleSketchName', 'ExampleSketchName.{ino,pde}]. Was: ${segments}`
|
||||
);
|
||||
}
|
||||
if (segments.length === 2) {
|
||||
return parent;
|
||||
}
|
||||
const label = segments[0];
|
||||
const existingSketch = parent.sketches.find(
|
||||
(sketch) => sketch.name === label
|
||||
);
|
||||
if (existingSketch) {
|
||||
// If the container has a sketch with the same label, it cannot have a child container.
|
||||
// See above example about how to ignore nested sketches.
|
||||
return undefined;
|
||||
}
|
||||
let child = parent.children.find((child) => child.label === label);
|
||||
if (!child) {
|
||||
child = SketchContainer.create(label);
|
||||
parent.children.push(child);
|
||||
}
|
||||
return child;
|
||||
};
|
||||
for (const pathToSketchFile of pathToAllSketchFiles) {
|
||||
const relative = path.relative(root, pathToSketchFile);
|
||||
if (!relative) {
|
||||
console.warn(
|
||||
`Could not determine relative sketch path from the root <${root}> to the sketch <${pathToSketchFile}>. Skipping. Relative path was: ${relative}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const segments = relative.split(path.sep);
|
||||
if (segments.length < 2) {
|
||||
// folder name, and sketch name.
|
||||
console.warn(
|
||||
`Expected at least one segment relative path from the root <${root}> to the sketch <${pathToSketchFile}>. Skipping. Segments were: ${segments}.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// the folder name and the sketch name must match. For example, `Foo/foo.ino` is invalid.
|
||||
// drop the folder name from the sketch name, if `.ino` or `.pde` remains, it's valid
|
||||
const sketchName = segments[segments.length - 2];
|
||||
const sketchFilename = segments[segments.length - 1];
|
||||
const sketchFileExtension = segments[segments.length - 1].replace(
|
||||
new RegExp(sketchName),
|
||||
''
|
||||
);
|
||||
if (sketchFileExtension !== '.ino' && sketchFileExtension !== '.pde') {
|
||||
console.warn(
|
||||
`Mismatching sketch file <${sketchFilename}> and sketch folder name <${sketchName}>. Skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const child = getOrCreateChildContainer(container, segments);
|
||||
if (child) {
|
||||
child.sketches.push({
|
||||
name: sketchName,
|
||||
uri: FileUri.create(pathToSketchFile).toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
private async root(uri?: string | undefined): Promise<string> {
|
||||
return FileUri.fsPath(uri ?? (await this.sketchbookUri()));
|
||||
}
|
||||
|
||||
private async sketchbookUri(): Promise<string> {
|
||||
const { sketchDirUri } = await this.configService.getConfiguration();
|
||||
return sketchDirUri;
|
||||
}
|
||||
|
||||
async loadSketch(uri: string): Promise<SketchWithDetails> {
|
||||
await this.coreClientProvider.initialized;
|
||||
const { client, instance } = await this.coreClient();
|
||||
const req = new LoadSketchRequest();
|
||||
req.setSketchPath(FileUri.fsPath(uri));
|
||||
const requestSketchPath = FileUri.fsPath(uri);
|
||||
req.setSketchPath(requestSketchPath);
|
||||
req.setInstance(instance);
|
||||
const stat = new Deferred<fs.Stats | Error>();
|
||||
fs.lstat(requestSketchPath, (err, result) =>
|
||||
err ? stat.resolve(err) : stat.resolve(result)
|
||||
);
|
||||
const sketch = await new Promise<SketchWithDetails>((resolve, reject) => {
|
||||
client.loadSketch(req, async (err, resp) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
const sketchFolderPath = resp.getLocationPath();
|
||||
const { mtimeMs } = await promisify(fs.lstat)(sketchFolderPath);
|
||||
const responseSketchPath = maybeNormalizeDrive(resp.getLocationPath());
|
||||
if (requestSketchPath !== responseSketchPath) {
|
||||
console.warn(
|
||||
`Warning! The request sketch path was different than the response sketch path from the CLI. This could be a potential bug. Request: <${requestSketchPath}>, response: <${responseSketchPath}>.`
|
||||
);
|
||||
}
|
||||
const resolvedStat = await stat.promise;
|
||||
if (resolvedStat instanceof Error) {
|
||||
console.error(
|
||||
`The CLI could load the sketch from ${requestSketchPath}, but stating the folder has failed.`
|
||||
);
|
||||
reject(resolvedStat);
|
||||
return;
|
||||
}
|
||||
const { mtimeMs } = resolvedStat;
|
||||
resolve({
|
||||
name: path.basename(sketchFolderPath),
|
||||
uri: FileUri.create(sketchFolderPath).toString(),
|
||||
name: path.basename(responseSketchPath),
|
||||
uri: FileUri.create(responseSketchPath).toString(),
|
||||
mainFileUri: FileUri.create(resp.getMainFile()).toString(),
|
||||
otherSketchFileUris: resp
|
||||
.getOtherSketchFilesList()
|
||||
@@ -292,12 +361,18 @@ export class SketchesServiceImpl extends CoreClientAware
|
||||
];
|
||||
const today = new Date();
|
||||
const parentPath = await new Promise<string>((resolve, reject) => {
|
||||
temp.mkdir({ prefix }, (err, dirPath) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
temp.mkdir({ prefix }, (createError, dirPath) => {
|
||||
if (createError) {
|
||||
reject(createError);
|
||||
return;
|
||||
}
|
||||
resolve(dirPath);
|
||||
fs.realpath.native(dirPath, (resolveError, resolvedDirPath) => {
|
||||
if (resolveError) {
|
||||
reject(resolveError);
|
||||
return;
|
||||
}
|
||||
resolve(resolvedDirPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
const sketchBaseName = `sketch_${
|
||||
@@ -395,20 +470,21 @@ void loop() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async isTemp(sketch: Sketch): Promise<boolean> {
|
||||
let sketchPath = FileUri.fsPath(sketch.uri);
|
||||
let temp = await promisify(fs.realpath)(os.tmpdir());
|
||||
// Note: VS Code URI normalizes the drive letter. `C:` will be converted into `c:`.
|
||||
// https://github.com/Microsoft/vscode/issues/68325#issuecomment-462239992
|
||||
if (isWindows) {
|
||||
if (WIN32_DRIVE_REGEXP.exec(sketchPath)) {
|
||||
sketchPath = firstToLowerCase(sketchPath);
|
||||
}
|
||||
if (WIN32_DRIVE_REGEXP.exec(temp)) {
|
||||
temp = firstToLowerCase(temp);
|
||||
}
|
||||
}
|
||||
return sketchPath.indexOf(prefix) !== -1 && sketchPath.startsWith(temp);
|
||||
async isTemp(sketch: SketchRef): Promise<boolean> {
|
||||
// Consider the following paths:
|
||||
// macOS:
|
||||
// - Temp folder: /var/folders/k3/d2fkvv1j16v3_rz93k7f74180000gn/T
|
||||
// - Sketch folder: /private/var/folders/k3/d2fkvv1j16v3_rz93k7f74180000gn/T/arduino-ide2-A0337D47F86B24A51DF3DBCF2CC17925
|
||||
// Windows:
|
||||
// - Temp folder: C:\Users\KITTAA~1\AppData\Local\Temp
|
||||
// - Sketch folder: c:\Users\kittaakos\AppData\Local\Temp\.arduinoIDE-unsaved2022431-21824-116kfaz.9ljl\sketch_may31a
|
||||
// Both sketches are valid and temp, but this function will give a false-negative result if we use the default `os.tmpdir()` logic.
|
||||
const sketchPath = maybeNormalizeDrive(FileUri.fsPath(sketch.uri));
|
||||
const tempPath = this.tempDirRealpath; // https://github.com/sindresorhus/temp-dir
|
||||
const result =
|
||||
sketchPath.indexOf(prefix) !== -1 && sketchPath.startsWith(tempPath);
|
||||
console.log('isTemp?', result, sketch.uri);
|
||||
return result;
|
||||
}
|
||||
|
||||
async copy(
|
||||
@@ -512,10 +588,15 @@ void loop() {
|
||||
interface SketchWithDetails extends Sketch {
|
||||
readonly mtimeMs: number;
|
||||
}
|
||||
interface SketchContainerWithDetails extends SketchContainer {
|
||||
readonly label: string;
|
||||
readonly children: SketchContainerWithDetails[];
|
||||
readonly sketches: SketchWithDetails[];
|
||||
|
||||
/**
|
||||
* If on Windows, will change the input `C:\\path\\to\\somewhere` to `c:\\path\\to\\somewhere`.
|
||||
*/
|
||||
function maybeNormalizeDrive(input: string): string {
|
||||
if (isWindows && WIN32_DRIVE_REGEXP.test(input)) {
|
||||
return firstToLowerCase(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -523,7 +604,7 @@ interface SketchContainerWithDetails extends SketchContainer {
|
||||
* from other new sketches I created today.
|
||||
* If 'sketch_jul8a' is already used, go with 'sketch_jul8b'.
|
||||
* If 'sketch_jul8b' already used, go with 'sketch_jul8c'.
|
||||
* When it reacheas 'sketch_jul8z', go with 'sketch_jul8aa',
|
||||
* When it reach 'sketch_jul8z', go with 'sketch_jul8aa',
|
||||
* and so on.
|
||||
*/
|
||||
function sketchIndexToLetters(num: number): string {
|
||||
|
||||
@@ -11,9 +11,9 @@ export class BackendApplication extends TheiaBackendApplication {
|
||||
constructor(
|
||||
@inject(ContributionProvider)
|
||||
@named(BackendApplicationContribution)
|
||||
protected readonly contributionsProvider: ContributionProvider<BackendApplicationContribution>,
|
||||
protected override readonly contributionsProvider: ContributionProvider<BackendApplicationContribution>,
|
||||
@inject(BackendApplicationCliContribution)
|
||||
protected readonly cliParams: BackendApplicationCliContribution
|
||||
protected override readonly cliParams: BackendApplicationCliContribution
|
||||
) {
|
||||
super(contributionsProvider, cliParams);
|
||||
// Workaround for Electron not installing a handler to ignore SIGPIPE
|
||||
|
||||
@@ -7,7 +7,7 @@ import { EnvVariablesServerImpl as TheiaEnvVariablesServerImpl } from '@theia/co
|
||||
|
||||
@injectable()
|
||||
export class EnvVariablesServer extends TheiaEnvVariablesServerImpl {
|
||||
protected readonly configDirUri = Promise.resolve(
|
||||
protected override readonly configDirUri = Promise.resolve(
|
||||
FileUri.create(
|
||||
join(homedir(), BackendApplicationConfigProvider.get().configDirName)
|
||||
).toString()
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import findGit from 'find-git-exec';
|
||||
import { dirname } from 'path';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import { GitInit } from '@theia/git/lib/node/init/git-init';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
|
||||
@injectable()
|
||||
export class DefaultGitInit implements GitInit {
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
async init(): Promise<void> {
|
||||
const { env } = process;
|
||||
try {
|
||||
const { execPath, path, version } = await findGit();
|
||||
if (!!execPath && !!path && !!version) {
|
||||
const dir = dirname(dirname(path));
|
||||
const [execPathOk, pathOk, dirOk] = await Promise.all([
|
||||
pathExists(execPath),
|
||||
pathExists(path),
|
||||
pathExists(dir),
|
||||
]);
|
||||
if (execPathOk && pathOk && dirOk) {
|
||||
if (
|
||||
typeof env.LOCAL_GIT_DIRECTORY !== 'undefined' &&
|
||||
env.LOCAL_GIT_DIRECTORY !== dir
|
||||
) {
|
||||
console.error(
|
||||
`Misconfigured env.LOCAL_GIT_DIRECTORY: ${env.LOCAL_GIT_DIRECTORY}. dir was: ${dir}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
typeof env.GIT_EXEC_PATH !== 'undefined' &&
|
||||
env.GIT_EXEC_PATH !== execPath
|
||||
) {
|
||||
console.error(
|
||||
`Misconfigured env.GIT_EXEC_PATH: ${env.GIT_EXEC_PATH}. execPath was: ${execPath}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
process.env.LOCAL_GIT_DIRECTORY = dir;
|
||||
process.env.GIT_EXEC_PATH = execPath;
|
||||
console.info(`Using Git [${version}] from the PATH. (${path})`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { promises as fs, constants } from 'fs';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { DefaultWorkspaceServer as TheiaDefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
|
||||
import { ConfigService } from '../../../common/protocol/config-service';
|
||||
import { SketchesService } from '../../../common/protocol';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
|
||||
@injectable()
|
||||
export class DefaultWorkspaceServer extends TheiaDefaultWorkspaceServer {
|
||||
@@ -11,13 +14,49 @@ export class DefaultWorkspaceServer extends TheiaDefaultWorkspaceServer {
|
||||
@inject(ILogger)
|
||||
protected readonly logger: ILogger;
|
||||
|
||||
protected async getWorkspaceURIFromCli(): Promise<string | undefined> {
|
||||
@inject(SketchesService)
|
||||
private readonly sketchesService: SketchesService;
|
||||
|
||||
override async onStart(): Promise<void> {
|
||||
// NOOP
|
||||
// No need to remove untitled workspaces. IDE2 does not use workspaces.
|
||||
}
|
||||
|
||||
override async getMostRecentlyUsedWorkspace(): Promise<string | undefined> {
|
||||
const uri = await super.getMostRecentlyUsedWorkspace();
|
||||
if (!uri) {
|
||||
const { uri } = await this.sketchesService.createNewSketch();
|
||||
return uri;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the async re-implementation of the default Theia behavior.
|
||||
*/
|
||||
override async getRecentWorkspaces(): Promise<string[]> {
|
||||
const listUri: string[] = [];
|
||||
const data = await this.readRecentWorkspacePathsFromUserHome();
|
||||
if (data && data.recentRoots) {
|
||||
await Promise.all(
|
||||
data.recentRoots
|
||||
.filter((element) => Boolean(element))
|
||||
.map(async (element) => {
|
||||
if (await this.exists(element)) {
|
||||
listUri.push(element);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
return listUri;
|
||||
}
|
||||
|
||||
private async exists(uri: string): Promise<boolean> {
|
||||
try {
|
||||
const config = await this.configService.getConfiguration();
|
||||
return config.sketchDirUri;
|
||||
} catch (err) {
|
||||
this.logger.error(`Failed to determine the sketch directory: ${err}`);
|
||||
return super.getWorkspaceURIFromCli();
|
||||
await fs.access(FileUri.fsPath(uri), constants.R_OK | constants.W_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user