Skip to content

Commit 553e8d6

Browse files
author
Benjamin Pasero
committed
progress - allow to report total/worked in viewlets/panels
1 parent 405e4f0 commit 553e8d6

2 files changed

Lines changed: 72 additions & 60 deletions

File tree

src/vs/platform/progress/common/progress.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ export interface IProgress<T> {
9292
report(item: T): void;
9393
}
9494

95-
export const emptyProgress: IProgress<any> = Object.freeze({ report() { } });
96-
9795
export class Progress<T> implements IProgress<T> {
9896

9997
private _callback: (data: T) => void;

src/vs/workbench/services/progress/browser/progressService.ts

Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import 'vs/css!./media/progressService';
77

88
import { localize } from 'vs/nls';
9-
import { IDisposable, dispose, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
10-
import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions } from 'vs/platform/progress/common/progress';
9+
import { IDisposable, dispose, DisposableStore, MutableDisposable, Disposable } from 'vs/base/common/lifecycle';
10+
import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, ILocalProgressService } from 'vs/platform/progress/common/progress';
1111
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
1212
import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
1313
import { timeout } from 'vs/base/common/async';
@@ -26,90 +26,88 @@ import { EventHelper } from 'vs/base/browser/dom';
2626
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
2727
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
2828

29-
export class ProgressService implements IProgressService, IDisposable {
29+
export class ProgressService extends Disposable implements IProgressService {
3030

3131
_serviceBrand: ServiceIdentifier<IProgressService>;
3232

33-
private readonly _stack: [IProgressOptions, Progress<IProgressStep>][] = [];
34-
private readonly _globalStatusEntry = new MutableDisposable();
33+
private readonly stack: [IProgressOptions, Progress<IProgressStep>][] = [];
34+
private readonly globalStatusEntry = this._register(new MutableDisposable());
3535

3636
constructor(
37-
@IActivityService private readonly _activityBar: IActivityService,
38-
@IViewletService private readonly _viewletService: IViewletService,
39-
@IPanelService private readonly _panelService: IPanelService,
40-
@INotificationService private readonly _notificationService: INotificationService,
41-
@IStatusbarService private readonly _statusbarService: IStatusbarService,
42-
@ILayoutService private readonly _layoutService: ILayoutService,
43-
@IThemeService private readonly _themeService: IThemeService,
44-
@IKeybindingService private readonly _keybindingService: IKeybindingService
45-
) { }
46-
47-
dispose() {
48-
this._globalStatusEntry.dispose();
37+
@IActivityService private readonly activityService: IActivityService,
38+
@IViewletService private readonly viewletService: IViewletService,
39+
@IPanelService private readonly panelService: IPanelService,
40+
@INotificationService private readonly notificationService: INotificationService,
41+
@IStatusbarService private readonly statusbarService: IStatusbarService,
42+
@ILayoutService private readonly layoutService: ILayoutService,
43+
@IThemeService private readonly themeService: IThemeService,
44+
@IKeybindingService private readonly keybindingService: IKeybindingService
45+
) {
46+
super();
4947
}
5048

5149
withProgress<R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => Promise<R>, onDidCancel?: () => void): Promise<R> {
5250
const { location } = options;
5351
if (typeof location === 'string') {
54-
if (this._viewletService.getProgressIndicator(location)) {
55-
return this._withViewletProgress(location, task, { ...options, location });
52+
if (this.viewletService.getProgressIndicator(location)) {
53+
return this.withViewletProgress(location, task, { ...options, location });
5654
}
5755

58-
if (this._panelService.getProgressIndicator(location)) {
59-
return this._withPanelProgress(location, task, { ...options, location });
56+
if (this.panelService.getProgressIndicator(location)) {
57+
return this.withPanelProgress(location, task, { ...options, location });
6058
}
6159

6260
return Promise.reject(new Error(`Bad progress location: ${location}`));
6361
}
6462

6563
switch (location) {
6664
case ProgressLocation.Notification:
67-
return this._withNotificationProgress({ ...options, location }, task, onDidCancel);
65+
return this.withNotificationProgress({ ...options, location }, task, onDidCancel);
6866
case ProgressLocation.Window:
69-
return this._withWindowProgress(options, task);
67+
return this.withWindowProgress(options, task);
7068
case ProgressLocation.Explorer:
71-
return this._withViewletProgress('workbench.view.explorer', task, { ...options, location });
69+
return this.withViewletProgress('workbench.view.explorer', task, { ...options, location });
7270
case ProgressLocation.Scm:
73-
return this._withViewletProgress('workbench.view.scm', task, { ...options, location });
71+
return this.withViewletProgress('workbench.view.scm', task, { ...options, location });
7472
case ProgressLocation.Extensions:
75-
return this._withViewletProgress('workbench.view.extensions', task, { ...options, location });
73+
return this.withViewletProgress('workbench.view.extensions', task, { ...options, location });
7674
case ProgressLocation.Dialog:
77-
return this._withDialogProgress(options, task, onDidCancel);
75+
return this.withDialogProgress(options, task, onDidCancel);
7876
default:
7977
return Promise.reject(new Error(`Bad progress location: ${location}`));
8078
}
8179
}
8280

83-
private _withWindowProgress<R = unknown>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise<R>): Promise<R> {
84-
const task: [IProgressOptions, Progress<IProgressStep>] = [options, new Progress<IProgressStep>(() => this._updateWindowProgress())];
81+
private withWindowProgress<R = unknown>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise<R>): Promise<R> {
82+
const task: [IProgressOptions, Progress<IProgressStep>] = [options, new Progress<IProgressStep>(() => this.updateWindowProgress())];
8583

8684
const promise = callback(task[1]);
8785

8886
let delayHandle: any = setTimeout(() => {
8987
delayHandle = undefined;
90-
this._stack.unshift(task);
91-
this._updateWindowProgress();
88+
this.stack.unshift(task);
89+
this.updateWindowProgress();
9290

9391
// show progress for at least 150ms
9492
Promise.all([
9593
timeout(150),
9694
promise
9795
]).finally(() => {
98-
const idx = this._stack.indexOf(task);
99-
this._stack.splice(idx, 1);
100-
this._updateWindowProgress();
96+
const idx = this.stack.indexOf(task);
97+
this.stack.splice(idx, 1);
98+
this.updateWindowProgress();
10199
});
102100
}, 150);
103101

104102
// cancel delay if promise finishes below 150ms
105103
return promise.finally(() => clearTimeout(delayHandle));
106104
}
107105

108-
private _updateWindowProgress(idx: number = 0) {
109-
this._globalStatusEntry.clear();
106+
private updateWindowProgress(idx: number = 0) {
107+
this.globalStatusEntry.clear();
110108

111-
if (idx < this._stack.length) {
112-
const [options, progress] = this._stack[idx];
109+
if (idx < this.stack.length) {
110+
const [options, progress] = this.stack[idx];
113111

114112
let progressTitle = options.title;
115113
let progressMessage = progress.value && progress.value.message;
@@ -133,18 +131,18 @@ export class ProgressService implements IProgressService, IDisposable {
133131

134132
} else {
135133
// no title, no message -> no progress. try with next on stack
136-
this._updateWindowProgress(idx + 1);
134+
this.updateWindowProgress(idx + 1);
137135
return;
138136
}
139137

140-
this._globalStatusEntry.value = this._statusbarService.addEntry({
138+
this.globalStatusEntry.value = this.statusbarService.addEntry({
141139
text: `$(sync~spin) ${text}`,
142140
tooltip: title
143141
}, 'status.progress', localize('status.progress', "Progress Message"), StatusbarAlignment.LEFT);
144142
}
145143
}
146144

147-
private _withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P {
145+
private withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P {
148146
const toDispose = new DisposableStore();
149147

150148
const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => {
@@ -174,7 +172,7 @@ export class ProgressService implements IProgressService, IDisposable {
174172
}
175173

176174
const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions };
177-
const handle = this._notificationService.notify({
175+
const handle = this.notificationService.notify({
178176
severity: Severity.Info,
179177
message,
180178
source: options.source,
@@ -241,21 +239,17 @@ export class ProgressService implements IProgressService, IDisposable {
241239
return promise;
242240
}
243241

244-
private _withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<{ message?: string }>) => P, options: IProgressCompositeOptions): P {
245-
const promise = task(emptyProgress);
242+
private withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P {
246243

247244
// show in viewlet
248-
const viewletProgress = this._viewletService.getProgressIndicator(viewletId);
249-
if (viewletProgress) {
250-
viewletProgress.showWhile(promise, options.delay);
251-
}
245+
const promise = this.withCompositeProgress(this.viewletService.getProgressIndicator(viewletId), task, options);
252246

253247
// show activity bar
254248
let activityProgress: IDisposable;
255249
let delayHandle: any = setTimeout(() => {
256250
delayHandle = undefined;
257251

258-
const handle = this._activityBar.showActivity(
252+
const handle = this.activityService.showActivity(
259253
viewletId,
260254
new ProgressBadge(() => ''),
261255
'progress-badge',
@@ -286,19 +280,39 @@ export class ProgressService implements IProgressService, IDisposable {
286280
return promise;
287281
}
288282

289-
private _withPanelProgress<P extends Promise<R>, R = unknown>(panelid: string, task: (progress: IProgress<{ message?: string }>) => P, options: IProgressCompositeOptions): P {
290-
const promise = task(emptyProgress);
283+
private withPanelProgress<P extends Promise<R>, R = unknown>(panelid: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P {
291284

292285
// show in panel
293-
const panelProgress = this._panelService.getProgressIndicator(panelid);
294-
if (panelProgress) {
295-
panelProgress.showWhile(promise, options.delay);
286+
return this.withCompositeProgress(this.panelService.getProgressIndicator(panelid), task, options);
287+
}
288+
289+
private withCompositeProgress<P extends Promise<R>, R = unknown>(compositeProgressService: ILocalProgressService | null, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P {
290+
let progressRunner: IProgressRunner | undefined = undefined;
291+
292+
const promise = task({
293+
report: progress => {
294+
if (!progressRunner) {
295+
return;
296+
}
297+
298+
if (typeof progress.increment === 'number') {
299+
progressRunner.worked(progress.increment);
300+
}
301+
}
302+
});
303+
304+
if (compositeProgressService) {
305+
if (typeof options.total === 'number') {
306+
progressRunner = compositeProgressService.show(options.total, options.delay);
307+
} else {
308+
compositeProgressService.showWhile(promise, options.delay);
309+
}
296310
}
297311

298312
return promise;
299313
}
300314

301-
private _withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P {
315+
private withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => P, onDidCancel?: () => void): P {
302316
const disposables = new DisposableStore();
303317
const allowableCommands = [
304318
'workbench.action.quit',
@@ -309,13 +323,13 @@ export class ProgressService implements IProgressService, IDisposable {
309323

310324
const createDialog = (message: string) => {
311325
dialog = new Dialog(
312-
this._layoutService.container,
326+
this.layoutService.container,
313327
message,
314328
[options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")],
315329
{
316330
type: 'pending',
317331
keyEventProcessor: (event: StandardKeyboardEvent) => {
318-
const resolved = this._keybindingService.softDispatch(event, this._layoutService.container);
332+
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
319333
if (resolved && resolved.commandId) {
320334
if (allowableCommands.indexOf(resolved.commandId) === -1) {
321335
EventHelper.stop(event, true);
@@ -326,7 +340,7 @@ export class ProgressService implements IProgressService, IDisposable {
326340
);
327341

328342
disposables.add(dialog);
329-
disposables.add(attachDialogStyler(dialog, this._themeService));
343+
disposables.add(attachDialogStyler(dialog, this.themeService));
330344

331345
dialog.show().then(() => {
332346
if (typeof onDidCancel === 'function') {

0 commit comments

Comments
 (0)