Speed up IDE startup time.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta
2022-05-20 12:11:23 +02:00
committed by Akos Kitta
parent cb50d3a70d
commit 4c55807392
179 changed files with 2692 additions and 2022 deletions

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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> {

View File

@@ -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)
);

View File

@@ -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,
}
}

View File

@@ -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;
};

View File

@@ -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,
}
}

View File

@@ -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);

View File

@@ -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,
}
}

View File

@@ -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) {

View File

@@ -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,
}
}

View File

@@ -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);
};

View File

@@ -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,
}
}

View File

@@ -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);
};

View File

@@ -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,
};
}

View File

@@ -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);
});
}

View File

@@ -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}`;
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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()

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}