diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index c3830290c6f4e453f51ab0bbeab193e9ebc9a851..f3e60f24455840d66083af6eab59d91315f44c0f 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -6,6 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IAction } from 'vs/base/common/actions'; export const IProgressService = createDecorator('progressService'); @@ -42,6 +43,12 @@ export interface IProgressOptions { cancellable?: boolean; } +export interface IProgressNotificationOptions extends IProgressOptions { + location: ProgressLocation.Notification; + primaryActions?: IAction[]; + secondaryActions?: IAction[]; +} + export interface IProgressStep { message?: string; increment?: number; diff --git a/src/vs/workbench/api/browser/mainThreadProgress.ts b/src/vs/workbench/api/browser/mainThreadProgress.ts index 2c889ccc709fee1d510b41be869f484b0e44bf3d..7c45dd4b1b5e64cae1634afd771f21af6980ec4a 100644 --- a/src/vs/workbench/api/browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/browser/mainThreadProgress.ts @@ -3,9 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProgress, IProgressService2, IProgressStep, IProgressOptions } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressService2, IProgressStep, ProgressLocation, IProgressOptions, IProgressNotificationOptions } from 'vs/platform/progress/common/progress'; import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { Action } from 'vs/base/common/actions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { localize } from 'vs/nls'; + +class ManageExtensionAction extends Action { + constructor(id: ExtensionIdentifier, label: string, commandService: ICommandService) { + super(id.value, label, undefined, true, () => { + return commandService.executeCommand('_extensions.manage', id.value); + }); + } +} @extHostNamedCustomer(MainContext.MainThreadProgress) export class MainThreadProgress implements MainThreadProgressShape { @@ -16,7 +28,8 @@ export class MainThreadProgress implements MainThreadProgressShape { constructor( extHostContext: IExtHostContext, - @IProgressService2 progressService: IProgressService2 + @IProgressService2 progressService: IProgressService2, + @ICommandService private readonly _commandService: ICommandService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostProgress); this._progressService = progressService; @@ -27,9 +40,19 @@ export class MainThreadProgress implements MainThreadProgressShape { this._progress.clear(); } - $startProgress(handle: number, options: IProgressOptions): void { + $startProgress(handle: number, options: IProgressOptions, extension?: IExtensionDescription): void { const task = this._createTask(handle); + if (options.location === ProgressLocation.Notification && extension && !extension.isUnderDevelopment) { + const notificationOptions: IProgressNotificationOptions = { + ...options, + location: ProgressLocation.Notification, + secondaryActions: [new ManageExtensionAction(extension.identifier, localize('manageExtension', "Manage Extension"), this._commandService)] + }; + + options = notificationOptions; + } + this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle)); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 85d79bd25f2e53d5fcaf8edb704258d005106424..0515075538c4d52bbaf8676411d60f112529dfeb 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -376,7 +376,7 @@ export interface MainThreadOutputServiceShape extends IDisposable { export interface MainThreadProgressShape extends IDisposable { - $startProgress(handle: number, options: IProgressOptions): void; + $startProgress(handle: number, options: IProgressOptions, extension?: IExtensionDescription): void; $progressReport(handle: number, message: IProgressStep): void; $progressEnd(handle: number): void; } diff --git a/src/vs/workbench/api/common/extHostProgress.ts b/src/vs/workbench/api/common/extHostProgress.ts index eb82d3d6a5c0d604c404acff7bbac1c9b939ceb5..afb0fb647f88f165474fc7f41941362bdce20d73 100644 --- a/src/vs/workbench/api/common/extHostProgress.ts +++ b/src/vs/workbench/api/common/extHostProgress.ts @@ -26,7 +26,7 @@ export class ExtHostProgress implements ExtHostProgressShape { const handle = this._handles++; const { title, location, cancellable } = options; const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name); - this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }); + this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }, extension); return this._withProgress(handle, task, !!cancellable); } diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index 3f9970c335b2ecc4e80ddd34f3bf3e1300e2131b..e63175a1188cf560f062d3863af8cbe196638700 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/progressService2'; import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress } from 'vs/platform/progress/common/progress'; +import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress, IProgressNotificationOptions } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { timeout } from 'vs/base/common/async'; @@ -54,7 +54,7 @@ export class ProgressService2 implements IProgressService2 { switch (location) { case ProgressLocation.Notification: - return this._withNotificationProgress(options, task, onDidCancel); + return this._withNotificationProgress({ ...options, location: ProgressLocation.Notification }, task, onDidCancel); case ProgressLocation.Window: return this._withWindowProgress(options, task); case ProgressLocation.Explorer: @@ -138,7 +138,7 @@ export class ProgressService2 implements IProgressService2 { } } - private _withNotificationProgress

, R = unknown>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { + private _withNotificationProgress

, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { const toDispose: IDisposable[] = []; const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { @@ -146,7 +146,7 @@ export class ProgressService2 implements IProgressService2 { return undefined; // we need a message at least } - const actions: INotificationActions = { primary: [] }; + const actions: INotificationActions = { primary: options.primaryActions || [], secondary: options.secondaryActions || [] }; if (options.cancellable) { const cancelAction = new class extends Action { constructor() {