Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CancellationToken logic for withProgress API #11027

Merged
merged 5 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@ export interface NotificationExt {
options: ProgressOptions,
task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => PromiseLike<R>
): PromiseLike<R>;
$acceptProgressCanceled(progressId: string): void;
}

export interface ScmCommandArg {
Expand Down Expand Up @@ -1812,6 +1813,7 @@ export const PLUGIN_RPC_CONTEXT = {
STATUS_BAR_MESSAGE_REGISTRY_MAIN: <ProxyIdentifier<StatusBarMessageRegistryMain>>createProxyIdentifier<StatusBarMessageRegistryMain>('StatusBarMessageRegistryMain'),
ENV_MAIN: createProxyIdentifier<EnvMain>('EnvMain'),
NOTIFICATION_MAIN: createProxyIdentifier<NotificationMain>('NotificationMain'),
NOTIFICATION_EXT: createProxyIdentifier<NotificationExt>('NotificationExt'),
TERMINAL_MAIN: createProxyIdentifier<TerminalServiceMain>('TerminalServiceMain'),
TREE_VIEWS_MAIN: createProxyIdentifier<TreeViewsMain>('TreeViewsMain'),
PREFERENCE_REGISTRY_MAIN: createProxyIdentifier<PreferenceRegistryMain>('PreferenceRegistryMain'),
Expand Down Expand Up @@ -1843,6 +1845,7 @@ export const MAIN_RPC_CONTEXT = {
QUICK_OPEN_EXT: createProxyIdentifier<QuickOpenExt>('QuickOpenExt'),
WINDOW_STATE_EXT: createProxyIdentifier<WindowStateExt>('WindowStateExt'),
NOTIFICATION_EXT: createProxyIdentifier<NotificationExt>('NotificationExt'),
NOTIFICATION_MAIN: createProxyIdentifier<NotificationMain>('NotificationMain'),
matt-dot marked this conversation as resolved.
Show resolved Hide resolved
WORKSPACE_EXT: createProxyIdentifier<WorkspaceExt>('WorkspaceExt'),
TEXT_EDITORS_EXT: createProxyIdentifier<TextEditorsExt>('TextEditorsExt'),
EDITORS_AND_DOCUMENTS_EXT: createProxyIdentifier<EditorsAndDocumentsExt>('EditorsAndDocumentsExt'),
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/main/browser/main-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { CustomEditorsMainImpl } from './custom-editors/custom-editors-main';
import { SecretsMainImpl } from './secrets-main';
import { WebviewViewsMainImpl } from './webview-views/webview-views-main';
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
import { NotificationExtImpl } from '../../plugin/notification';

export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const authenticationMain = new AuthenticationMainImpl(rpc, container);
Expand Down Expand Up @@ -108,6 +109,9 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
const notificationMain = new NotificationMainImpl(rpc, container);
rpc.set(PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, notificationMain);

const notificationExt = new NotificationExtImpl(rpc);
rpc.set(MAIN_RPC_CONTEXT.NOTIFICATION_EXT, notificationExt);

const terminalMain = new TerminalServiceMainImpl(rpc, container);
rpc.set(PLUGIN_RPC_CONTEXT.TERMINAL_MAIN, terminalMain);

Expand Down
17 changes: 13 additions & 4 deletions packages/plugin-ext/src/main/browser/notification-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,41 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************

import { NotificationMain } from '../../common/plugin-api-rpc';
import { NotificationExt, NotificationMain, MAIN_RPC_CONTEXT } from '../../common';
import { ProgressService, Progress, ProgressMessage } from '@theia/core/lib/common';
import { interfaces } from '@theia/core/shared/inversify';
import { RPCProtocol } from '../../common/rpc-protocol';
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';

export class NotificationMainImpl implements NotificationMain, Disposable {

private readonly progressService: ProgressService;
private readonly progressMap = new Map<string, Progress>();
private readonly progress2Work = new Map<string, number>();
private readonly proxy: NotificationExt;

protected readonly toDispose = new DisposableCollection(
Disposable.create(() => { /* mark as not disposed */ })
);

constructor(rpc: RPCProtocol, container: interfaces.Container) {
this.progressService = container.get(ProgressService);
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTIFICATION_EXT);
}

dispose(): void {
this.toDispose.dispose();
}

async $startProgress(options: NotificationMain.StartProgressOptions): Promise<string> {
const onDidCancel = () => {
// If the map does not contain current id, it has already stopped and should not be cancelled
if (this.progressMap.has(id)) {
this.proxy.$acceptProgressCanceled(id);
}
};

const progressMessage = this.mapOptions(options);
const progress = await this.progressService.showProgress(progressMessage);
const progress = await this.progressService.showProgress(progressMessage, onDidCancel);
const id = progress.id;
this.progressMap.set(id, progress);
this.progress2Work.set(id, 0);
Expand All @@ -58,10 +66,11 @@ export class NotificationMainImpl implements NotificationMain, Disposable {

$stopProgress(id: string): void {
const progress = this.progressMap.get(id);

if (progress) {
progress.cancel();
this.progressMap.delete(id);
this.progress2Work.delete(id);
progress.cancel();
}
}

Expand Down
20 changes: 18 additions & 2 deletions packages/plugin-ext/src/plugin/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';

export class NotificationExtImpl implements NotificationExt {
private readonly proxy: NotificationMain;
private mapProgressIdToCancellationSource: Map<string, CancellationTokenSource> = new Map();

constructor(rpc: RPCProtocol) {
this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN);
Expand All @@ -32,13 +33,20 @@ export class NotificationExtImpl implements NotificationExt {
options: ProgressOptions,
task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => PromiseLike<R>
): Promise<R> {
const source = new CancellationTokenSource();
const id = new Deferred<string>();
colin-grant-work marked this conversation as resolved.
Show resolved Hide resolved
const tokenSource = new CancellationTokenSource();
const progress = task({ report: async item => this.proxy.$updateProgress(await id.promise, item)}, tokenSource.token);
const progress = task({ report: async item => this.proxy.$updateProgress(await id.promise, item)}, source.token);
const title = options.title ? options.title : '';
const location = this.mapLocation(options.location);
const cancellable = options.cancellable;

id.resolve(await this.proxy.$startProgress({ title, location, cancellable }));

if (cancellable) {
const progressId = await id.promise;
this.mapProgressIdToCancellationSource.set(progressId, source);
}

const stop = async () => this.proxy.$stopProgress(await id.promise);
const promise = Promise.all([
progress,
Expand All @@ -48,6 +56,14 @@ export class NotificationExtImpl implements NotificationExt {
return progress;
}

public $acceptProgressCanceled(id: string): void {
const source = this.mapProgressIdToCancellationSource.get(id);
if (source) {
source.cancel();
this.mapProgressIdToCancellationSource.delete(id);
}
}

protected mapLocation(location: ProgressLocation | { viewId: string }): string | undefined {
if (typeof location === 'object') {
return location.viewId;
Expand Down