ATL-374: Refactored the Output services.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2020-09-15 18:00:17 +02:00
committed by Akos Kitta
parent f26dae185b
commit 5f5193932f
53 changed files with 829 additions and 948 deletions

View File

@@ -9,7 +9,7 @@ import { Event, Emitter } from '@theia/core/lib/common/event';
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, ArduinoDaemonClient, ToolOutputServiceServer } from '../common/protocol';
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
import { DaemonLog } from './daemon-log';
import { CLI_CONFIG } from './cli-config';
import { getExecPath, spawnCommand } from './exec-util';
@@ -21,13 +21,12 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
@named('daemon')
protected readonly logger: ILogger
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
@inject(EnvVariablesServer)
protected readonly envVariablesServer: EnvVariablesServer;
protected readonly clients: Array<ArduinoDaemonClient> = [];
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
protected readonly toDispose = new DisposableCollection();
protected readonly onDaemonStartedEmitter = new Emitter<void>();
protected readonly onDaemonStoppedEmitter = new Emitter<void>();
@@ -42,37 +41,6 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
this.startDaemon();
}
onStop(): void {
this.dispose();
}
// JSON-RPC proxy
setClient(client: ArduinoDaemonClient | undefined): void {
if (client) {
if (this._running) {
client.notifyStarted()
} else {
client.notifyStopped();
}
this.clients.push(client);
}
}
dispose(): void {
this.toDispose.dispose();
this.clients.length = 0;
}
disposeClient(client: ArduinoDaemonClient): void {
const index = this.clients.indexOf(client);
if (index === -1) {
this.logger.warn('Could not dispose client. It was not registered or was already disposed.');
} else {
this.clients.splice(index, 1);
}
}
// Daemon API
async isRunning(): Promise<boolean> {
@@ -184,7 +152,7 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
if (code === 0 || signal === 'SIGINT' || signal === 'SIGKILL') {
this.onData('Daemon has stopped.');
} else {
this.onData(`Daemon exited with ${typeof code === 'undefined' ? `signal '${signal}'` : `exit code: ${code}`}.`, { useOutput: false });
this.onData(`Daemon exited with ${typeof code === 'undefined' ? `signal '${signal}'` : `exit code: ${code}`}.`);
}
});
daemon.on('error', error => {
@@ -198,9 +166,7 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
this._running = true;
this._ready.resolve();
this.onDaemonStartedEmitter.fire();
for (const client of this.clients) {
client.notifyStarted();
}
this.notificationService.notifyDaemonStarted();
}
protected fireDaemonStopped(): void {
@@ -211,15 +177,10 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
this._ready.reject(); // Reject all pending.
this._ready = new Deferred<void>();
this.onDaemonStoppedEmitter.fire();
for (const client of this.clients) {
client.notifyStopped();
}
this.notificationService.notifyDaemonStopped();
}
protected onData(message: string, options: { useOutput: boolean } = { useOutput: true }): void {
if (options.useOutput) {
this.toolOutputService.append({ tool: 'daemon', chunk: DaemonLog.toPrettyString(message) });
}
protected onData(message: string): void {
DaemonLog.log(this.logger, message);
}

View File

@@ -5,130 +5,169 @@ import { ContainerModule } from 'inversify';
import { ArduinoDaemonImpl } from './arduino-daemon-impl';
import { ILogger } from '@theia/core/lib/common/logger';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { LibraryServiceServerPath, LibraryServiceServer, LibraryServiceClient } from '../common/protocol/library-service';
import { BoardsService, BoardsServicePath, BoardsServiceClient } from '../common/protocol/boards-service';
import { LibraryServiceServerImpl } from './library-service-server-impl';
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
import { LibraryServiceImpl } from './library-service-server-impl';
import { BoardsServiceImpl } from './boards-service-impl';
import { CoreServiceImpl } from './core-service-impl';
import { CoreService, CoreServicePath, CoreServiceClient } from '../common/protocol/core-service';
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
import { CoreClientProvider } from './core-client-provider';
import { ToolOutputService, ToolOutputServiceClient, ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core';
import { ToolOutputServiceServerImpl } from './tool-output-service-impl';
import { DefaultWorkspaceServerExt } from './default-workspace-server-ext';
import { WorkspaceServer } from '@theia/workspace/lib/common';
import { ConnectionHandler, JsonRpcConnectionHandler, JsonRpcProxy } from '@theia/core';
import { DefaultWorkspaceServer } from './theia/workspace/default-workspace-server';
import { WorkspaceServer as TheiaWorkspaceServer } from '@theia/workspace/lib/common';
import { SketchesServiceImpl } from './sketches-service-impl';
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
import { ConfigService, ConfigServicePath, ConfigServiceClient } from '../common/protocol/config-service';
import { ArduinoDaemon, ArduinoDaemonPath, ArduinoDaemonClient } from '../common/protocol/arduino-daemon';
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
import { ArduinoDaemon, ArduinoDaemonPath } from '../common/protocol/arduino-daemon';
import { MonitorServiceImpl } from './monitor/monitor-service-impl';
import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service';
import { MonitorClientProvider } from './monitor/monitor-client-provider';
import { ConfigServiceImpl } from './config-service-impl';
import { ArduinoHostedPluginReader } from './arduino-plugin-reader';
import { HostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
import { ConfigFileValidator } from './config-file-validator';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { ArduinoEnvVariablesServer } from './arduino-env-variables-server';
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { EnvVariablesServer } from './theia/env-variables/env-variables-server';
import { NodeFileSystemExt } from './node-filesystem-ext';
import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext';
import { ExamplesServiceImpl } from './examples-service-impl';
import { ExamplesService, ExamplesServicePath } from '../common/protocol/examples-service';
import { ExecutableService, ExecutableServicePath } from '../common/protocol/executable-service';
import { ExecutableServiceImpl } from './executable-service-impl';
import { OutputServicePath, OutputService } from '../common/protocol/output-service';
import { NotificationServiceServerImpl } from './notification-service-server';
import { NotificationServiceServer, NotificationServiceClient, NotificationServicePath } from '../common/protocol';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(EnvVariablesServer).to(ArduinoEnvVariablesServer).inSingletonScope();
bind(ConfigFileValidator).toSelf().inSingletonScope();
// XXX: The config service must start earlier than the daemon, hence the binding order does matter.
// Shared config service
bind(ConfigFileValidator).toSelf().inSingletonScope();
bind(ConfigServiceImpl).toSelf().inSingletonScope();
bind(ConfigService).toService(ConfigServiceImpl);
// Note: The config service must start earlier than the daemon, hence the binding order of the BA contribution does matter.
bind(BackendApplicationContribution).toService(ConfigServiceImpl);
bind(ConnectionHandler).toDynamicValue(context =>
new JsonRpcConnectionHandler<ConfigServiceClient>(ConfigServicePath, client => {
const server = context.container.get<ConfigServiceImpl>(ConfigServiceImpl);
server.setClient(client);
client.onDidCloseConnection(() => server.disposeClient(client));
return server;
})
).inSingletonScope();
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ConfigServicePath, () => context.container.get(ConfigService))).inSingletonScope();
// Shared daemon
bind(ArduinoDaemonImpl).toSelf().inSingletonScope();
bind(ArduinoDaemon).toService(ArduinoDaemonImpl);
bind(BackendApplicationContribution).toService(ArduinoDaemonImpl);
bind(ConnectionHandler).toDynamicValue(context =>
new JsonRpcConnectionHandler<ArduinoDaemonClient>(ArduinoDaemonPath, async client => {
const server = context.container.get<ArduinoDaemonImpl>(ArduinoDaemonImpl);
server.setClient(client);
client.onDidCloseConnection(() => server.disposeClient(client));
return server;
})
).inSingletonScope();
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ArduinoDaemonPath, () => context.container.get(ArduinoDaemon))).inSingletonScope();
// Shared examples service
bind(ExamplesServiceImpl).toSelf().inSingletonScope();
bind(ExamplesService).toService(ExamplesServiceImpl);
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ExamplesServicePath, () => context.container.get(ExamplesService))).inSingletonScope();
// Examples service. One per backend, each connected FE gets a proxy.
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
// const ExamplesServiceProxy = Symbol('ExamplesServiceProxy');
// bind(ExamplesServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(ExamplesService), {}));
// bindBackendService(ExamplesServicePath, ExamplesServiceProxy);
bind(ExamplesServiceImpl).toSelf().inSingletonScope();
bind(ExamplesService).toService(ExamplesServiceImpl);
bindBackendService(ExamplesServicePath, ExamplesService);
}));
// Exposes the executable paths/URIs to the frontend
bind(ExecutableServiceImpl).toSelf().inSingletonScope();
bind(ExecutableService).toService(ExecutableServiceImpl);
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ExecutableServicePath, () => context.container.get(ExecutableService))).inSingletonScope();
// Library service
bind(LibraryServiceServerImpl).toSelf().inSingletonScope();
bind(LibraryServiceServer).toService(LibraryServiceServerImpl);
bind(ConnectionHandler).toDynamicValue(context =>
new JsonRpcConnectionHandler<LibraryServiceClient>(LibraryServiceServerPath, client => {
const server = context.container.get<LibraryServiceServerImpl>(LibraryServiceServerImpl);
server.setClient(client);
client.onDidCloseConnection(() => server.dispose());
return server;
})
).inSingletonScope();
// Library service. Singleton per backend, each connected FE gets its proxy.
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
// const LibraryServiceProxy = Symbol('LibraryServiceProxy');
// bind(LibraryServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(LibraryService), {}));
// bindBackendService(LibraryServicePath, LibraryServiceProxy);
bind(LibraryServiceImpl).toSelf().inSingletonScope();
bind(LibraryService).toService(LibraryServiceImpl);
bindBackendService(LibraryServicePath, LibraryService);
}));
// Shred sketches service
bind(SketchesServiceImpl).toSelf().inSingletonScope();
bind(SketchesService).toService(SketchesServiceImpl);
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(SketchesServicePath, () => context.container.get(SketchesService))).inSingletonScope();
// Boards service
const boardsServiceConnectionModule = ConnectionContainerModule.create(async ({ bind, bindBackendService }) => {
// Boards service. One singleton per backend that does the board and port polling. Each connected FE gets its proxy.
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
// const BoardsServiceProxy = Symbol('BoardsServiceProxy');
// bind(BoardsServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(BoardsService), {}));
// bindBackendService(BoardsServicePath, BoardsServiceProxy);
bind(BoardsServiceImpl).toSelf().inSingletonScope();
bind(BoardsService).toService(BoardsServiceImpl);
bindBackendService<BoardsService, BoardsServiceClient>(BoardsServicePath, BoardsService, (service, client) => {
service.setClient(client);
bindBackendService<BoardsServiceImpl, JsonRpcProxy<object>>(BoardsServicePath, BoardsService, (service, client) => {
client.onDidCloseConnection(() => service.dispose());
return service;
});
});
bind(ConnectionContainerModule).toConstantValue(boardsServiceConnectionModule);
}));
// Shared Arduino core client provider service for the backend.
bind(CoreClientProvider).toSelf().inSingletonScope();
// Core service -> `verify` and `upload`. One per Theia connection.
const connectionConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
// Core service -> `verify` and `upload`. Singleton per BE, each FE connection gets its proxy.
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
// const CoreServiceProxy = Symbol('CoreServiceProxy');
// bind(CoreServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(CoreService), {}));
// bindBackendService(CoreServicePath, CoreServiceProxy);
bind(CoreServiceImpl).toSelf().inSingletonScope();
bind(CoreService).toService(CoreServiceImpl);
bindBackendService(BoardsServicePath, BoardsService);
bindBackendService<CoreService, CoreServiceClient>(CoreServicePath, CoreService, (service, client) => {
bindBackendService(CoreServicePath, CoreService);
}));
// #region Theia customizations
bind(DefaultWorkspaceServer).toSelf().inSingletonScope();
rebind(TheiaWorkspaceServer).toService(DefaultWorkspaceServer);
bind(EnvVariablesServer).toSelf().inSingletonScope();
rebind(TheiaEnvVariablesServer).toService(EnvVariablesServer);
bind(HostedPluginReader).toSelf().inSingletonScope();
rebind(TheiaHostedPluginReader).toService(HostedPluginReader);
// #endregion Theia customizations
// Shared monitor client provider service for the backend.
bind(MonitorClientProvider).toSelf().inSingletonScope();
bind(MonitorServiceImpl).toSelf().inSingletonScope();
bind(MonitorService).toService(MonitorServiceImpl);
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
const MonitorServiceProxy = Symbol('MonitorServiceProxy');
bind(MonitorServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(MonitorService), {}));
bindBackendService<MonitorService, MonitorServiceClient>(MonitorServicePath, MonitorServiceProxy, (service, client) => {
service.setClient(client);
client.onDidCloseConnection(() => service.dispose());
return service;
});
});
bind(ConnectionContainerModule).toConstantValue(connectionConnectionModule);
}));
// Tool output service -> feedback from the daemon, compile and flash
bind(ToolOutputServiceServerImpl).toSelf().inSingletonScope();
bind(ToolOutputServiceServer).toService(ToolOutputServiceServerImpl);
// Set up cpp extension
if (!process.env.CPP_CLANGD_COMMAND) {
const segments = ['..', '..', 'build'];
if (os.platform() === 'win32') {
segments.push('clangd.exe');
} else {
segments.push('bin');
segments.push('clangd');
}
const clangdCommand = join(__dirname, ...segments);
if (fs.existsSync(clangdCommand)) {
process.env.CPP_CLANGD_COMMAND = clangdCommand;
}
}
// File-system extension for mapping paths to URIs
bind(NodeFileSystemExt).toSelf().inSingletonScope();
bind(FileSystemExt).toService(NodeFileSystemExt);
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt))).inSingletonScope();
// Output service per connection.
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bindFrontendService }) => {
bindFrontendService(OutputServicePath, OutputService);
}));
// Notify all connected frontend instances
bind(NotificationServiceServerImpl).toSelf().inSingletonScope();
bind(NotificationServiceServer).toService(NotificationServiceServerImpl);
bind(ConnectionHandler).toDynamicValue(context =>
new JsonRpcConnectionHandler<ToolOutputServiceClient>(ToolOutputService.SERVICE_PATH, client => {
const server = context.container.get<ToolOutputServiceServer>(ToolOutputServiceServer);
new JsonRpcConnectionHandler<NotificationServiceClient>(NotificationServicePath, client => {
const server = context.container.get<NotificationServiceServer>(NotificationServiceServer);
server.setClient(client);
client.onDidCloseConnection(() => server.disposeClient(client));
return server;
@@ -153,52 +192,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
return parentLogger.child('config');
}).inSingletonScope().whenTargetNamed('config');
// Default workspace server extension to initialize and use a fallback workspace.
// If nothing was set previously.
bind(DefaultWorkspaceServerExt).toSelf().inSingletonScope();
rebind(WorkspaceServer).toService(DefaultWorkspaceServerExt);
// Shared monitor client provider service for the backend.
bind(MonitorClientProvider).toSelf().inSingletonScope();
// Connection scoped service for the serial monitor.
const monitorServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(MonitorServiceImpl).toSelf().inSingletonScope();
bind(MonitorService).toService(MonitorServiceImpl);
bindBackendService<MonitorService, MonitorServiceClient>(MonitorServicePath, MonitorService, (service, client) => {
service.setClient(client);
client.onDidCloseConnection(() => service.dispose());
return service;
});
});
bind(ConnectionContainerModule).toConstantValue(monitorServiceConnectionModule);
// Logger for the monitor service.
bind(ILogger).toDynamicValue(ctx => {
const parentLogger = ctx.container.get<ILogger>(ILogger);
return parentLogger.child('monitor-service');
}).inSingletonScope().whenTargetNamed('monitor-service');
// Set up cpp extension
if (!process.env.CPP_CLANGD_COMMAND) {
const segments = ['..', '..', 'build'];
if (os.platform() === 'win32') {
segments.push('clangd.exe');
} else {
segments.push('bin');
segments.push('clangd');
}
const clangdCommand = join(__dirname, ...segments);
if (fs.existsSync(clangdCommand)) {
process.env.CPP_CLANGD_COMMAND = clangdCommand;
}
}
bind(ArduinoHostedPluginReader).toSelf().inSingletonScope();
rebind(HostedPluginReader).toService(ArduinoHostedPluginReader);
// File-system extension for mapping paths to URIs
bind(NodeFileSystemExt).toSelf().inSingletonScope();
bind(FileSystemExt).toService(NodeFileSystemExt);
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt))).inSingletonScope();
});

View File

@@ -1,19 +1,22 @@
import { injectable, inject, postConstruct, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { BoardsService, BoardsPackage, Board, BoardsServiceClient, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer } from '../common/protocol';
import {
BoardsService,
BoardsPackage, Board, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer, OutputService, NotificationServiceServer, AttachedBoardsChangeEvent
} from '../common/protocol';
import {
PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq,
PlatformListResp, Platform, PlatformUninstallResp, PlatformUninstallReq
} from './cli-protocol/commands/core_pb';
import { CoreClientProvider } from './core-client-provider';
import { BoardListReq, BoardListResp, BoardDetailsReq, BoardDetailsResp } from './cli-protocol/commands/board_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Installable } from '../common/protocol/installable';
import { ListProgrammersAvailableForUploadReq, ListProgrammersAvailableForUploadResp } from './cli-protocol/commands/upload_pb';
import { Disposable } from '@theia/core/lib/common/disposable';
@injectable()
export class BoardsServiceImpl implements BoardsService {
export class BoardsServiceImpl implements BoardsService, Disposable {
@inject(ILogger)
protected logger: ILogger;
@@ -25,8 +28,11 @@ export class BoardsServiceImpl implements BoardsService {
@inject(CoreClientProvider)
protected readonly coreClientProvider: CoreClientProvider;
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
@inject(OutputService)
protected readonly outputService: OutputService;
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
protected discoveryInitialized = false;
protected discoveryTimer: NodeJS.Timer | undefined;
@@ -38,7 +44,6 @@ export class BoardsServiceImpl implements BoardsService {
protected attachedBoards: Board[] = [];
protected availablePorts: Port[] = [];
protected started = new Deferred<void>();
protected client: BoardsServiceClient | undefined;
@postConstruct()
protected async init(): Promise<void> {
@@ -49,19 +54,19 @@ export class BoardsServiceImpl implements BoardsService {
const update = (oldBoards: Board[], newBoards: Board[], oldPorts: Port[], newPorts: Port[], message: string) => {
this.attachedBoards = newBoards;
this.availablePorts = newPorts;
this.discoveryLogger.info(`${message} - Discovered boards: ${JSON.stringify(newBoards)} and available ports: ${JSON.stringify(newPorts)}`);
if (this.client) {
this.client.notifyAttachedBoardsChanged({
oldState: {
boards: oldBoards,
ports: oldPorts
},
newState: {
boards: newBoards,
ports: newPorts
}
});
}
const event = {
oldState: {
boards: oldBoards,
ports: oldPorts
},
newState: {
boards: newBoards,
ports: newPorts
}
};
this.discoveryLogger.info(`${message}`);
this.discoveryLogger.info(`${AttachedBoardsChangeEvent.toString(event)}`);
this.notificationService.notifyAttachedBoardsChanged(event);
}
const sortedBoards = boards.sort(Board.compare);
const sortedPorts = ports.sort(Port.compare);
@@ -103,16 +108,13 @@ export class BoardsServiceImpl implements BoardsService {
}, 1000);
}
setClient(client: BoardsServiceClient | undefined): void {
this.client = client;
}
dispose(): void {
this.logger.info('>>> Disposing boards service...');
if (this.discoveryTimer !== undefined) {
this.logger.info('>>> Disposing the boards discovery...');
clearInterval(this.discoveryTimer);
this.logger.info('<<< Disposed the boards discovery.');
}
this.client = undefined;
this.logger.info('<<< Disposed boards service.');
}
@@ -213,14 +215,28 @@ export class BoardsServiceImpl implements BoardsService {
const detailsReq = new BoardDetailsReq();
detailsReq.setInstance(instance);
detailsReq.setFqbn(fqbn);
const detailsResp = await new Promise<BoardDetailsResp>((resolve, reject) => client.boardDetails(detailsReq, (err, resp) => {
const detailsResp = await new Promise<BoardDetailsResp | undefined>((resolve, reject) => client.boardDetails(detailsReq, (err, resp) => {
if (err) {
// Required cores are not installed manually: https://github.com/arduino/arduino-cli/issues/954
if (err.message.indexOf('missing platform release') !== -1 && err.message.indexOf('referenced by board') !== -1) {
resolve(undefined);
return;
}
reject(err);
return;
}
resolve(resp);
}));
if (!detailsResp) {
return {
fqbn,
configOptions: [],
programmers: [],
requiredTools: []
};
}
const requiredTools = detailsResp.getToolsdependenciesList().map(t => <Tool>{
name: t.getName(),
packager: t.getPackager(),
@@ -391,18 +407,17 @@ export class BoardsServiceImpl implements BoardsService {
resp.on('data', (r: PlatformInstallResp) => {
const prog = r.getProgress();
if (prog && prog.getFile()) {
this.toolOutputService.append({ tool: 'board download', chunk: `downloading ${prog.getFile()}\n` });
this.outputService.append({ name: 'board download', chunk: `downloading ${prog.getFile()}\n` });
}
});
await new Promise<void>((resolve, reject) => {
resp.on('end', resolve);
resp.on('error', reject);
});
if (this.client) {
const items = await this.search({});
const updated = items.find(other => BoardsPackage.equals(other, item)) || item;
this.client.notifyInstalled({ item: updated });
}
const items = await this.search({});
const updated = items.find(other => BoardsPackage.equals(other, item)) || item;
this.notificationService.notifyPlatformInstalled({ item: updated });
console.info('<<< Boards package installation done.', item);
}
@@ -426,7 +441,7 @@ export class BoardsServiceImpl implements BoardsService {
const resp = client.platformUninstall(req);
resp.on('data', (_: PlatformUninstallResp) => {
if (!logged) {
this.toolOutputService.append({ tool: 'board uninstall', chunk: `uninstalling ${item.id}\n` });
this.outputService.append({ name: 'board uninstall', chunk: `uninstalling ${item.id}\n` });
logged = true;
}
})
@@ -434,10 +449,9 @@ export class BoardsServiceImpl implements BoardsService {
resp.on('end', resolve);
resp.on('error', reject);
});
if (this.client) {
// Here, unlike at `install` we send out the argument `item`. Otherwise, we would not know about the board FQBN.
this.client.notifyUninstalled({ item });
}
// Here, unlike at `install` we send out the argument `item`. Otherwise, we would not know about the board FQBN.
this.notificationService.notifyPlatformUninstalled({ item });
console.info('<<< Boards package uninstallation done.', item);
}

View File

@@ -8,7 +8,7 @@ import { ILogger } from '@theia/core/lib/common/logger';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { ConfigService, Config, ConfigServiceClient } from '../common/protocol';
import { ConfigService, Config, NotificationServiceServer } from '../common/protocol';
import * as fs from './fs-extra';
import { spawnCommand } from './exec-util';
import { RawData } from './cli-protocol/settings/settings_pb';
@@ -38,10 +38,12 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
@inject(ArduinoDaemonImpl)
protected readonly daemon: ArduinoDaemonImpl;
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
protected updating = false;
protected config: Config;
protected cliConfig: DefaultCliConfig | undefined;
protected clients: Array<ConfigServiceClient> = [];
protected ready = new Deferred<void>();
protected readonly configChangeEmitter = new Emitter<Config>();
@@ -94,25 +96,6 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
return this.getConfiguration().then(({ sketchDirUri }) => new URI(sketchDirUri).isEqualOrParent(new URI(uri)));
}
setClient(client: ConfigServiceClient | undefined): void {
if (client) {
this.clients.push(client);
}
}
dispose(): void {
this.clients.length = 0;
}
disposeClient(client: ConfigServiceClient): void {
const index = this.clients.indexOf(client);
if (index === -1) {
this.logger.warn('Could not dispose client. It was not registered or was already disposed.');
} else {
this.clients.splice(index, 1);
}
}
protected async loadCliConfig(): Promise<DefaultCliConfig | undefined> {
const cliConfigFileUri = await this.getCliConfigFileUri();
const cliConfigPath = FileUri.fsPath(cliConfigFileUri);
@@ -214,9 +197,7 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
this.cliConfig = cliConfig;
this.config = config;
this.configChangeEmitter.fire(this.config);
for (const client of this.clients) {
client.notifyConfigChanged(this.config);
}
this.notificationService.notifyConfigChanged({ config: this.config });
}).finally(() => this.updating = false);
} catch (err) {
this.logger.error('Failed to update the daemon with the current CLI configuration.', err);
@@ -227,15 +208,11 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
}
protected fireConfigChanged(config: Config): void {
for (const client of this.clients) {
client.notifyConfigChanged(config);
}
this.notificationService.notifyConfigChanged({ config });
}
protected fireInvalidConfig(): void {
for (const client of this.clients) {
client.notifyInvalidConfig();
}
this.notificationService.notifyConfigChanged({ config: undefined });
}
protected async unwatchCliConfig(): Promise<void> {

View File

@@ -1,26 +1,21 @@
import * as grpc from '@grpc/grpc-js';
import { inject, injectable } from 'inversify';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { ToolOutputServiceServer } from '../common/protocol';
import { GrpcClientProvider } from './grpc-client-provider';
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
import * as commandsGrpcPb from './cli-protocol/commands/commands_grpc_pb';
import { Instance } from './cli-protocol/commands/common_pb';
import { InitReq, InitResp, UpdateIndexReq, UpdateIndexResp, UpdateLibrariesIndexResp, UpdateLibrariesIndexReq } from './cli-protocol/commands/commands_pb';
import { NotificationServiceServer } from '../common/protocol';
@injectable()
export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Client> {
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
protected readonly onIndexUpdatedEmitter = new Emitter<void>();
protected readonly onClientReadyEmitter = new Emitter<void>();
get onIndexUpdated(): Event<void> {
return this.onIndexUpdatedEmitter.event;
}
get onClientReady(): Event<void> {
return this.onClientReadyEmitter.event;
}
@@ -76,11 +71,11 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
indexUpdateSucceeded = true;
break;
} catch (e) {
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating index in attempt ${i}: ${e}`, severity: 'error' });
console.error(`Error while updating index in attempt ${i}.`, e);
}
}
if (!indexUpdateSucceeded) {
this.toolOutputService.append({ tool: 'daemon', chunk: 'Was unable to update the index. Please restart to try again.', severity: 'error' });
console.error('Could not update the index. Please restart to try again.');
}
let libIndexUpdateSucceeded = true;
@@ -90,15 +85,15 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
libIndexUpdateSucceeded = true;
break;
} catch (e) {
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating library index in attempt ${i}: ${e}`, severity: 'error' });
console.error(`Error while updating library index in attempt ${i}.`, e);
}
}
if (!libIndexUpdateSucceeded) {
this.toolOutputService.append({ tool: 'daemon', chunk: `Was unable to update the library index. Please restart to try again.`, severity: 'error' });
console.error('Could not update the library index. Please restart to try again.');
}
if (indexUpdateSucceeded && libIndexUpdateSucceeded) {
this.onIndexUpdatedEmitter.fire();
this.notificationService.notifyIndexUpdated();
}
}
@@ -116,12 +111,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
if (progress.getCompleted()) {
if (file) {
if (/\s/.test(file)) {
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
console.log(`${file} completed.`);
} else {
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
console.log(`Download of '${file}' completed.`);
}
} else {
this.toolOutputService.append({ tool: 'daemon', chunk: `The library index has been successfully updated.\n'` });
console.log('The library index has been successfully updated.');
}
file = undefined;
}
@@ -147,12 +142,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
if (progress.getCompleted()) {
if (file) {
if (/\s/.test(file)) {
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
console.log(`${file} completed.`);
} else {
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
console.log(`Download of '${file}' completed.`);
}
} else {
this.toolOutputService.append({ tool: 'daemon', chunk: `The index has been successfully updated.\n'` });
console.log('The index has been successfully updated.');
}
file = undefined;
}

View File

@@ -1,12 +1,12 @@
import { inject, injectable, postConstruct } from 'inversify';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { CoreService, CoreServiceClient } from '../common/protocol/core-service';
import { inject, injectable } from 'inversify';
import { dirname } from 'path';
import { CoreService } from '../common/protocol/core-service';
import { CompileReq, CompileResp } from './cli-protocol/commands/compile_pb';
import { BoardsService } from '../common/protocol/boards-service';
import { CoreClientProvider } from './core-client-provider';
import * as path from 'path';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp } from './cli-protocol/commands/upload_pb';
import { OutputService } from '../common/protocol/output-service';
import { NotificationServiceServer } from '../common/protocol';
@injectable()
export class CoreServiceImpl implements CoreService {
@@ -14,29 +14,17 @@ export class CoreServiceImpl implements CoreService {
@inject(CoreClientProvider)
protected readonly coreClientProvider: CoreClientProvider;
@inject(OutputService)
protected readonly outputService: OutputService;
@inject(BoardsService)
protected readonly boardsService: BoardsService;
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
protected client: CoreServiceClient | undefined;
@postConstruct()
protected init(): void {
this.coreClientProvider.onIndexUpdated(() => {
if (this.client) {
this.client.notifyIndexUpdated();
}
})
}
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
async compile(options: CoreService.Compile.Options): Promise<void> {
this.toolOutputService.append({ tool: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
this.outputService.append({ name: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
const { sketchUri, fqbn } = options;
const sketchFilePath = FileUri.fsPath(sketchUri);
const sketchpath = path.dirname(sketchFilePath);
const sketchpath = dirname(sketchFilePath);
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
@@ -61,25 +49,25 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (cr: CompileResp) => {
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
this.toolOutputService.append({ tool: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' });
this.outputService.append({ name: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' });
} catch (e) {
this.toolOutputService.append({ tool: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' });
this.outputService.append({ name: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' });
throw e;
}
}
async upload(options: CoreService.Upload.Options): Promise<void> {
await this.compile(options);
this.toolOutputService.append({ tool: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
this.outputService.append({ name: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
const { sketchUri, fqbn } = options;
const sketchFilePath = FileUri.fsPath(sketchUri);
const sketchpath = path.dirname(sketchFilePath);
const sketchpath = dirname(sketchFilePath);
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
@@ -106,15 +94,15 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (resp: UploadResp) => {
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
this.toolOutputService.append({ tool: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
this.outputService.append({ name: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
} catch (e) {
this.toolOutputService.append({ tool: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
this.outputService.append({ name: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
throw e;
}
}
@@ -141,24 +129,16 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (resp: BurnBootloaderResp) => {
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
} catch (e) {
this.toolOutputService.append({ tool: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
this.outputService.append({ name: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
throw e;
}
}
setClient(client: CoreServiceClient | undefined): void {
this.client = client;
}
dispose(): void {
this.client = undefined;
}
}

View File

@@ -6,7 +6,7 @@ import { notEmpty } from '@theia/core/lib/common/objects';
import { Sketch } from '../common/protocol/sketches-service';
import { SketchesServiceImpl } from './sketches-service-impl';
import { ExamplesService, ExampleContainer } from '../common/protocol/examples-service';
import { LibraryServiceServer, LibraryLocation, LibraryPackage } from '../common/protocol';
import { LibraryLocation, LibraryPackage, LibraryService } from '../common/protocol';
import { ConfigServiceImpl } from './config-service-impl';
@injectable()
@@ -15,8 +15,8 @@ export class ExamplesServiceImpl implements ExamplesService {
@inject(SketchesServiceImpl)
protected readonly sketchesService: SketchesServiceImpl;
@inject(LibraryServiceServer)
protected readonly libraryService: LibraryServiceServer;
@inject(LibraryService)
protected readonly libraryService: LibraryService;
@inject(ConfigServiceImpl)
protected readonly configService: ConfigServiceImpl;
@@ -44,7 +44,7 @@ export class ExamplesServiceImpl implements ExamplesService {
const current: ExampleContainer[] = [];
const any: ExampleContainer[] = [];
if (fqbn) {
const packages = await this.libraryService.list({ fqbn });
const packages: LibraryPackage[] = await this.libraryService.list({ fqbn });
for (const pkg of packages) {
const container = await this.tryGroupExamples(pkg);
const { location } = pkg;

View File

@@ -1,5 +1,5 @@
import { injectable, inject, postConstruct } from 'inversify';
import { LibraryPackage, LibraryServiceClient, LibraryServiceServer } from '../common/protocol/library-service';
import { LibraryPackage, LibraryService } from '../common/protocol/library-service';
import { CoreClientProvider } from './core-client-provider';
import {
LibrarySearchReq,
@@ -14,14 +14,14 @@ import {
LibraryUninstallResp,
Library
} from './cli-protocol/commands/lib_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Installable } from '../common/protocol/installable';
import { ILogger, notEmpty } from '@theia/core';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { FileUri } from '@theia/core/lib/node';
import { OutputService, NotificationServiceServer } from '../common/protocol';
@injectable()
export class LibraryServiceServerImpl implements LibraryServiceServer {
export class LibraryServiceImpl implements LibraryService {
@inject(ILogger)
protected logger: ILogger;
@@ -29,11 +29,13 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
@inject(CoreClientProvider)
protected readonly coreClientProvider: CoreClientProvider;
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
@inject(OutputService)
protected readonly outputService: OutputService;
@inject(NotificationServiceServer)
protected readonly notificationServer: NotificationServiceServer;
protected ready = new Deferred<void>();
protected client: LibraryServiceClient | undefined;
@postConstruct()
protected init(): void {
@@ -108,7 +110,31 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
req.setFqbn(fqbn);
}
const resp = await new Promise<LibraryListResp>((resolve, reject) => client.libraryList(req, ((error, resp) => !!error ? reject(error) : resolve(resp))));
const resp = await new Promise<LibraryListResp | undefined>((resolve, reject) => {
client.libraryList(req, ((error, r) => {
if (error) {
const { message } = error;
// Required core dependency is missing.
// https://github.com/arduino/arduino-cli/issues/954
if (message.indexOf('missing platform release') !== -1 && message.indexOf('referenced by board') !== -1) {
resolve(undefined);
return;
}
// The core for the board is not installed, `lib list` cannot be filtered based on FQBN.
// https://github.com/arduino/arduino-cli/issues/955
if (message.indexOf('platform') !== -1 && message.indexOf('is not installed') !== -1) {
resolve(undefined);
return;
}
reject(error);
return;
}
resolve(r);
}));
});
if (!resp) {
return [];
}
return resp.getInstalledLibraryList().map(item => {
const library = item.getLibrary();
if (!library) {
@@ -151,7 +177,7 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
resp.on('data', (r: LibraryInstallResp) => {
const prog = r.getProgress();
if (prog) {
this.toolOutputService.append({ tool: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` });
this.outputService.append({ name: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` });
}
});
await new Promise<void>((resolve, reject) => {
@@ -159,12 +185,9 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
resp.on('error', reject);
});
if (this.client) {
const items = await this.search({});
const updated = items.find(other => LibraryPackage.equals(other, item)) || item;
this.client.notifyInstalled({ item: updated });
}
const items = await this.search({});
const updated = items.find(other => LibraryPackage.equals(other, item)) || item;
this.notificationServer.notifyLibraryInstalled({ item: updated });
console.info('<<< Library package installation done.', item);
}
@@ -186,7 +209,7 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
const resp = client.libraryUninstall(req);
resp.on('data', (_: LibraryUninstallResp) => {
if (!logged) {
this.toolOutputService.append({ tool: 'library', chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` });
this.outputService.append({ name: 'library', chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` });
logged = true;
}
});
@@ -194,19 +217,13 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
resp.on('end', resolve);
resp.on('error', reject);
});
if (this.client) {
this.client.notifyUninstalled({ item });
}
console.info('<<< Library package uninstallation done.', item);
}
setClient(client: LibraryServiceClient | undefined): void {
this.client = client;
this.notificationServer.notifyLibraryUninstalled({ item });
console.info('<<< Library package uninstallation done.', item);
}
dispose(): void {
this.logger.info('>>> Disposing library service...');
this.client = undefined;
this.logger.info('<<< Disposed library service.');
}

View File

@@ -0,0 +1,65 @@
import { injectable } from 'inversify';
import { NotificationServiceServer, NotificationServiceClient, AttachedBoardsChangeEvent, BoardsPackage, LibraryPackage, Config } from '../common/protocol';
@injectable()
export class NotificationServiceServerImpl implements NotificationServiceServer {
protected readonly clients: NotificationServiceClient[] = [];
notifyIndexUpdated(): void {
this.clients.forEach(client => client.notifyIndexUpdated());
}
notifyDaemonStarted(): void {
this.clients.forEach(client => client.notifyDaemonStarted());
}
notifyDaemonStopped(): void {
this.clients.forEach(client => client.notifyDaemonStopped());
}
notifyPlatformInstalled(event: { item: BoardsPackage }): void {
this.clients.forEach(client => client.notifyPlatformInstalled(event));
}
notifyPlatformUninstalled(event: { item: BoardsPackage }): void {
this.clients.forEach(client => client.notifyPlatformUninstalled(event));
}
notifyLibraryInstalled(event: { item: LibraryPackage }): void {
this.clients.forEach(client => client.notifyLibraryInstalled(event));
}
notifyLibraryUninstalled(event: { item: LibraryPackage }): void {
this.clients.forEach(client => client.notifyLibraryUninstalled(event));
}
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
this.clients.forEach(client => client.notifyAttachedBoardsChanged(event));
}
notifyConfigChanged(event: { config: Config | undefined }): void {
this.clients.forEach(client => client.notifyConfigChanged(event));
}
setClient(client: NotificationServiceClient): void {
this.clients.push(client);
}
disposeClient(client: NotificationServiceClient): void {
const index = this.clients.indexOf(client);
if (index === -1) {
console.warn(`Could not dispose notification service client. It was not registered.`);
return;
}
this.clients.splice(index, 1);
}
dispose(): void {
for (const client of this.clients) {
this.disposeClient(client);
}
this.clients.length = 0;
}
}

View File

@@ -2,11 +2,11 @@ import { join } from 'path';
import { homedir } from 'os';
import { injectable } from 'inversify';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { EnvVariablesServerImpl } from '@theia/core/lib/node/env-variables/env-variables-server';
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
import { EnvVariablesServerImpl as TheiaEnvVariablesServerImpl } from '@theia/core/lib/node/env-variables/env-variables-server';
@injectable()
export class ArduinoEnvVariablesServer extends EnvVariablesServerImpl {
export class EnvVariablesServer extends TheiaEnvVariablesServerImpl {
protected readonly configDirUri = Promise.resolve(FileUri.create(join(homedir(), BackendApplicationConfigProvider.get().configDirName)).toString());

View File

@@ -1,11 +1,11 @@
import { injectable, inject } from 'inversify';
import { HostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
import { PluginPackage, PluginContribution } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { CLI_CONFIG } from './cli-config';
import { ConfigServiceImpl } from './config-service-impl';
import { CLI_CONFIG } from '../../cli-config';
import { ConfigServiceImpl } from '../../config-service-impl';
@injectable()
export class ArduinoHostedPluginReader extends HostedPluginReader {
export class HostedPluginReader extends TheiaHostedPluginReader {
@inject(ConfigServiceImpl)
protected readonly configService: ConfigServiceImpl;

View File

@@ -1,10 +1,10 @@
import { injectable, inject } from 'inversify';
import { ILogger } from '@theia/core';
import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
import { ConfigService } from '../common/protocol/config-service';
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';
@injectable()
export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer {
export class DefaultWorkspaceServer extends TheiaDefaultWorkspaceServer {
@inject(ConfigService)
protected readonly configService: ConfigService;

View File

@@ -1,40 +0,0 @@
import { injectable } from 'inversify';
import { ToolOutputServiceServer, ToolOutputServiceClient, ToolOutputMessage } from '../common/protocol/tool-output-service';
@injectable()
export class ToolOutputServiceServerImpl implements ToolOutputServiceServer {
protected clients: ToolOutputServiceClient[] = [];
append(message: ToolOutputMessage): void {
if (!message.chunk) {
return;
}
for (const client of this.clients) {
client.onMessageReceived(message);
}
}
setClient(client: ToolOutputServiceClient | undefined): void {
if (!client) {
return;
}
this.clients.push(client);
}
disposeClient(client: ToolOutputServiceClient): void {
const index = this.clients.indexOf(client);
if (index === -1) {
console.warn(`Could not dispose tools output client. It was not registered.`);
return;
}
this.clients.splice(index, 1);
}
dispose(): void {
for (const client of this.clients) {
this.disposeClient(client);
}
this.clients.length = 0;
}
}