ATL-786: Progress indication for install.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2021-04-08 15:18:44 +02:00
committed by Akos Kitta
parent 8071298598
commit 9aff90b0af
30 changed files with 557 additions and 219 deletions

View File

@@ -11,5 +11,5 @@ export * from './searchable';
export * from './sketches-service';
export * from './examples-service';
export * from './executable-service';
export * from './output-service';
export * from './response-service';
export * from './notification-service';

View File

@@ -1,20 +1,26 @@
import * as semver from 'semver';
import { Progress } from '@theia/core/lib/common/message-service-protocol';
import { CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation';
import { naturalCompare } from './../utils';
import { ArduinoComponent } from './arduino-component';
import { MessageService } from '@theia/core';
import { ResponseServiceImpl } from '../../browser/response-service-impl';
export interface Installable<T extends ArduinoComponent> {
/**
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
*/
install(options: { item: T, version?: Installable.Version }): Promise<void>;
install(options: { item: T, progressId?: string, version?: Installable.Version }): Promise<void>;
/**
* Uninstalls the given component. It is a NOOP if not installed.
*/
uninstall(options: { item: T }): Promise<void>;
uninstall(options: { item: T, progressId?: string }): Promise<void>;
}
export namespace Installable {
export type Version = string;
export namespace Version {
/**
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
@@ -26,4 +32,70 @@ export namespace Installable {
return naturalCompare(left, right);
};
}
export async function installWithProgress<T extends ArduinoComponent>(options: {
installable: Installable<T>,
messageService: MessageService,
responseService: ResponseServiceImpl,
item: T,
version: Installable.Version
}): Promise<void> {
const { item, version } = options;
return doWithProgress({
...options,
progressText: `Processing ${item.name}:${version}`,
run: ({ progressId }) => options.installable.install({ item: options.item, version: options.version, progressId })
});
}
export async function uninstallWithProgress<T extends ArduinoComponent>(options: {
installable: Installable<T>,
messageService: MessageService,
responseService: ResponseServiceImpl,
item: T
}): Promise<void> {
const { item } = options;
return doWithProgress({
...options,
progressText: `Processing ${item.name}${item.installedVersion ? `:${item.installedVersion}` : ''}`,
run: ({ progressId }) => options.installable.uninstall({ item: options.item, progressId })
});
}
export async function doWithProgress(options: {
run: ({ progressId }: { progressId: string }) => Promise<void>,
messageService: MessageService,
responseService: ResponseServiceImpl,
progressText: string
}): Promise<void> {
return withProgress(options.progressText, options.messageService, async (progress, _) => {
const progressId = progress.id;
const toDispose = options.responseService.onProgressDidChange(progressMessage => {
if (progressId === progressMessage.progressId) {
const { message, work } = progressMessage;
progress.report({ message, work });
}
});
try {
options.responseService.clearArduinoChannel();
await options.run({ progressId });
} finally {
toDispose.dispose();
}
});
}
async function withProgress(text: string, messageService: MessageService, cb: (progress: Progress, token: CancellationToken) => Promise<void>): Promise<void> {
const cancellationSource = new CancellationTokenSource();
const { token } = cancellationSource;
const progress = await messageService.showProgress({ text, options: { cancelable: false } }, () => cancellationSource.cancel());
try {
await cb(progress, token);
} finally {
progress.cancel();
}
}
}

View File

@@ -9,8 +9,8 @@ export interface LibraryService extends Installable<LibraryPackage>, Searchable<
/**
* When `installDependencies` is not set, it is `true` by default. If you want to skip the installation of required dependencies, set it to `false`.
*/
install(options: { item: LibraryPackage, version?: Installable.Version, installDependencies?: boolean }): Promise<void>;
installZip(options: { zipUri: string, overwrite?: boolean }): Promise<void>;
install(options: { item: LibraryPackage, progressId?: string, version?: Installable.Version, installDependencies?: boolean }): Promise<void>;
installZip(options: { zipUri: string, progressId?: string, overwrite?: boolean }): Promise<void>;
/**
* Set `filterSelf` to `true` if you want to avoid having `item` in the result set.
* Note: as of today (22.02.2021), the CLI works like this: `./arduino-cli lib deps Adaino@0.1.0 ✕ Adaino 0.1.0 must be installed.`.

View File

@@ -1,10 +0,0 @@
export interface OutputMessage {
readonly chunk: string;
readonly severity?: 'error' | 'warning' | 'info'; // Currently not used!
}
export const OutputServicePath = '/services/output-service';
export const OutputService = Symbol('OutputService');
export interface OutputService {
append(message: OutputMessage): void;
}

View File

@@ -0,0 +1,23 @@
export interface OutputMessage {
readonly chunk: string;
readonly severity?: 'error' | 'warning' | 'info'; // Currently not used!
}
export interface ProgressMessage {
readonly progressId: string;
readonly message: string;
readonly work?: ProgressMessage.Work;
}
export namespace ProgressMessage {
export interface Work {
readonly done: number;
readonly total: number;
}
}
export const ResponseServicePath = '/services/response-service';
export const ResponseService = Symbol('ResponseService');
export interface ResponseService {
appendToOutput(message: OutputMessage): void;
reportProgress(message: ProgressMessage): void;
}