diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 4e5c40d6e4cd6..8aa4c89fd09b1 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -762,6 +762,7 @@ export interface NotificationExt { options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => PromiseLike ): PromiseLike; + $acceptProgressCanceled(progressId: string): void; } export interface ScmCommandArg { @@ -1812,6 +1813,7 @@ export const PLUGIN_RPC_CONTEXT = { STATUS_BAR_MESSAGE_REGISTRY_MAIN: >createProxyIdentifier('StatusBarMessageRegistryMain'), ENV_MAIN: createProxyIdentifier('EnvMain'), NOTIFICATION_MAIN: createProxyIdentifier('NotificationMain'), + NOTIFICATION_EXT: createProxyIdentifier('NotificationExt'), TERMINAL_MAIN: createProxyIdentifier('TerminalServiceMain'), TREE_VIEWS_MAIN: createProxyIdentifier('TreeViewsMain'), PREFERENCE_REGISTRY_MAIN: createProxyIdentifier('PreferenceRegistryMain'), @@ -1843,6 +1845,7 @@ export const MAIN_RPC_CONTEXT = { QUICK_OPEN_EXT: createProxyIdentifier('QuickOpenExt'), WINDOW_STATE_EXT: createProxyIdentifier('WindowStateExt'), NOTIFICATION_EXT: createProxyIdentifier('NotificationExt'), + NOTIFICATION_MAIN: createProxyIdentifier('NotificationMain'), WORKSPACE_EXT: createProxyIdentifier('WorkspaceExt'), TEXT_EDITORS_EXT: createProxyIdentifier('TextEditorsExt'), EDITORS_AND_DOCUMENTS_EXT: createProxyIdentifier('EditorsAndDocumentsExt'), diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index c5699790b864e..92cb03bc8ae23 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -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); @@ -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); diff --git a/packages/plugin-ext/src/main/browser/notification-main.ts b/packages/plugin-ext/src/main/browser/notification-main.ts index 64198a91dba8d..2014fcb235e2b 100644 --- a/packages/plugin-ext/src/main/browser/notification-main.ts +++ b/packages/plugin-ext/src/main/browser/notification-main.ts @@ -14,17 +14,17 @@ // 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(); private readonly progress2Work = new Map(); + private readonly proxy: NotificationExt; protected readonly toDispose = new DisposableCollection( Disposable.create(() => { /* mark as not disposed */ }) @@ -32,6 +32,7 @@ export class NotificationMainImpl implements NotificationMain, Disposable { constructor(rpc: RPCProtocol, container: interfaces.Container) { this.progressService = container.get(ProgressService); + this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTIFICATION_EXT); } dispose(): void { @@ -39,8 +40,15 @@ export class NotificationMainImpl implements NotificationMain, Disposable { } async $startProgress(options: NotificationMain.StartProgressOptions): Promise { + 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); @@ -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(); } } diff --git a/packages/plugin-ext/src/plugin/notification.ts b/packages/plugin-ext/src/plugin/notification.ts index 7cfba29de9404..45a44ded04548 100644 --- a/packages/plugin-ext/src/plugin/notification.ts +++ b/packages/plugin-ext/src/plugin/notification.ts @@ -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 = new Map(); constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN); @@ -32,13 +33,20 @@ export class NotificationExtImpl implements NotificationExt { options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => PromiseLike ): Promise { + const source = new CancellationTokenSource(); const id = new Deferred(); - 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, @@ -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;