Skip to content

Commit bbca381

Browse files
authored
Add a disposable base class (#6445)
* Add a disposable base class Fixes #6285 * PR feedback
1 parent 420a836 commit bbca381

72 files changed

Lines changed: 1078 additions & 1160 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3705,7 +3705,7 @@
37053705
"constants-browserify": "^1.0.0",
37063706
"crypto-browserify": "3.12.0",
37073707
"css-loader": "5.1.3",
3708-
"esbuild-loader": "2.10.0",
3708+
"esbuild-loader": "4.2.2",
37093709
"eslint": "7.22.0",
37103710
"eslint-cli": "1.1.1",
37113711
"eslint-plugin-import": "2.22.1",

src/api/api1.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import * as vscode from 'vscode';
77
import { APIState, PublishEvent } from '../@types/git';
8+
import { Disposable } from '../common/lifecycle';
89
import Logger from '../common/logger';
910
import { TernarySearchTree } from '../common/utils';
1011
import { API, IGit, PostCommitCommandsProvider, Repository, ReviewerCommentsProvider, TitleAndDescriptionProvider } from './api';
@@ -72,7 +73,7 @@ export const enum Status {
7273
BOTH_MODIFIED,
7374
}
7475

75-
export class GitApiImpl implements API, IGit, vscode.Disposable {
76+
export class GitApiImpl extends Disposable implements API, IGit {
7677
private static _handlePool: number = 0;
7778
private _providers = new Map<number, IGit>();
7879

@@ -111,11 +112,6 @@ export class GitApiImpl implements API, IGit, vscode.Disposable {
111112
private _onDidPublish = new vscode.EventEmitter<PublishEvent>();
112113
readonly onDidPublish: vscode.Event<PublishEvent> = this._onDidPublish.event;
113114

114-
private _disposables: vscode.Disposable[];
115-
constructor() {
116-
this._disposables = [];
117-
}
118-
119115
private _updateReposContext() {
120116
const reposCount = Array.from(this._providers.values()).reduce((prev, current) => {
121117
return prev + current.repositories.length;
@@ -128,17 +124,17 @@ export class GitApiImpl implements API, IGit, vscode.Disposable {
128124
const handle = this._nextHandle();
129125
this._providers.set(handle, provider);
130126

131-
this._disposables.push(provider.onDidCloseRepository(e => this._onDidCloseRepository.fire(e)));
132-
this._disposables.push(provider.onDidOpenRepository(e => {
127+
this._register(provider.onDidCloseRepository(e => this._onDidCloseRepository.fire(e)));
128+
this._register(provider.onDidOpenRepository(e => {
133129
Logger.appendLine(`Repository ${e.rootUri} has been opened`);
134130
this._updateReposContext();
135131
this._onDidOpenRepository.fire(e);
136132
}));
137133
if (provider.onDidChangeState) {
138-
this._disposables.push(provider.onDidChangeState(e => this._onDidChangeState.fire(e)));
134+
this._register(provider.onDidChangeState(e => this._onDidChangeState.fire(e)));
139135
}
140136
if (provider.onDidPublish) {
141-
this._disposables.push(provider.onDidPublish(e => this._onDidPublish.fire(e)));
137+
this._register(provider.onDidPublish(e => this._onDidPublish.fire(e)));
142138
}
143139

144140
this._updateReposContext();
@@ -188,18 +184,13 @@ export class GitApiImpl implements API, IGit, vscode.Disposable {
188184
return GitApiImpl._handlePool++;
189185
}
190186

191-
dispose() {
192-
this._disposables.forEach(disposable => disposable.dispose());
193-
}
194-
195187
private _titleAndDescriptionProviders: Set<{ title: string, provider: TitleAndDescriptionProvider }> = new Set();
196188
registerTitleAndDescriptionProvider(title: string, provider: TitleAndDescriptionProvider): vscode.Disposable {
197189
const registeredValue = { title, provider };
198190
this._titleAndDescriptionProviders.add(registeredValue);
199-
const disposable = {
191+
const disposable = this._register({
200192
dispose: () => this._titleAndDescriptionProviders.delete(registeredValue)
201-
};
202-
this._disposables.push(disposable);
193+
});
203194
return disposable;
204195
}
205196

@@ -219,10 +210,9 @@ export class GitApiImpl implements API, IGit, vscode.Disposable {
219210
registerReviewerCommentsProvider(title: string, provider: ReviewerCommentsProvider): vscode.Disposable {
220211
const registeredValue = { title, provider };
221212
this._reviewerCommentsProviders.add(registeredValue);
222-
const disposable = {
213+
const disposable = this._register({
223214
dispose: () => this._reviewerCommentsProviders.delete(registeredValue)
224-
};
225-
this._disposables.push(disposable);
215+
});
226216
return disposable;
227217
}
228218

src/common/authentication.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ export enum AuthProvider {
1515
}
1616

1717
export class AuthenticationError extends Error {
18-
name: string;
19-
stack?: string;
20-
constructor(public message: string) {
18+
constructor(message: string) {
2119
super(message);
2220
}
2321
}

src/common/lifecycle.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
8+
export function toDisposable(d: () => void): vscode.Disposable {
9+
return { dispose: d };
10+
}
11+
12+
export function combinedDisposable(disposables: vscode.Disposable[]): vscode.Disposable {
13+
return toDisposable(() => disposeAll(disposables));
14+
}
15+
16+
export function disposeAll(disposables: vscode.Disposable[]) {
17+
while (disposables.length) {
18+
const item = disposables.pop();
19+
item?.dispose();
20+
}
21+
}
22+
23+
export abstract class Disposable {
24+
protected _isDisposed = false;
25+
26+
private _disposables: vscode.Disposable[] = [];
27+
28+
public dispose(): any {
29+
if (this._isDisposed) {
30+
return;
31+
}
32+
this._isDisposed = true;
33+
disposeAll(this._disposables);
34+
this._disposables = [];
35+
}
36+
37+
protected _register<T extends vscode.Disposable>(value: T): T {
38+
if (this._isDisposed) {
39+
value.dispose();
40+
} else {
41+
this._disposables.push(value);
42+
}
43+
return value;
44+
}
45+
46+
protected get isDisposed() {
47+
return this._isDisposed;
48+
}
49+
}

src/common/logger.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55
import * as vscode from 'vscode';
6+
import { Disposable } from './lifecycle';
67

78
export const PR_TREE = 'PullRequestTree';
89

9-
class Log {
10-
private _outputChannel: vscode.LogOutputChannel;
11-
private _disposable: vscode.Disposable;
12-
private _activePerfMarkers: Map<string, number> = new Map();
10+
class Log extends Disposable {
11+
private readonly _outputChannel: vscode.LogOutputChannel;
12+
private readonly _activePerfMarkers: Map<string, number> = new Map();
1313

1414
constructor() {
15-
this._outputChannel = vscode.window.createOutputChannel('GitHub Pull Request', { log: true });
15+
super();
16+
this._outputChannel = this._register(vscode.window.createOutputChannel('GitHub Pull Request', { log: true }));
1617
}
1718

1819
public startPerfMarker(marker: string) {
@@ -59,12 +60,6 @@ class Log {
5960
public error(message: any, component?: string) {
6061
this._outputChannel.error(this.logString(message, component));
6162
}
62-
63-
public dispose() {
64-
if (this._disposable) {
65-
this._disposable.dispose();
66-
}
67-
}
6863
}
6964

7065
const Logger = new Log();

src/common/temporaryState.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55
import * as vscode from 'vscode';
6+
import { disposeAll } from './lifecycle';
67
import Logger from './logger';
7-
import { dispose } from './utils';
88

99
let tempState: TemporaryState | undefined;
1010

@@ -13,17 +13,17 @@ export class TemporaryState extends vscode.Disposable {
1313
private readonly disposables: vscode.Disposable[] = [];
1414
private readonly persistInSessionDisposables: vscode.Disposable[] = [];
1515

16-
constructor(private _storageUri: vscode.Uri) {
17-
super(() => this.disposables.forEach(disposable => disposable.dispose()));
16+
constructor(private readonly _storageUri: vscode.Uri) {
17+
super(() => disposeAll(this.disposables));
1818
}
1919

2020
private get path(): vscode.Uri {
2121
return vscode.Uri.joinPath(this._storageUri, this.SUBPATH);
2222
}
2323

24-
dispose() {
25-
dispose(this.disposables);
26-
dispose(this.persistInSessionDisposables);
24+
override dispose() {
25+
disposeAll(this.disposables);
26+
disposeAll(this.persistInSessionDisposables);
2727
}
2828

2929
private addDisposable(disposable: vscode.Disposable, persistInSession: boolean) {

src/common/utils.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dayjs from 'dayjs';
99
import * as relativeTime from 'dayjs/plugin/relativeTime';
1010
import * as updateLocale from 'dayjs/plugin/updateLocale';
1111
import type { Disposable, Event, ExtensionContext, Uri } from 'vscode';
12+
import { combinedDisposable } from './lifecycle';
1213
// TODO: localization for webview needed
1314

1415
dayjs.extend(relativeTime.default, {
@@ -65,19 +66,6 @@ export function uniqBy<T>(arr: T[], fn: (el: T) => string): T[] {
6566
});
6667
}
6768

68-
export function dispose<T extends Disposable>(disposables: T[]): T[] {
69-
disposables.forEach(d => d.dispose());
70-
return [];
71-
}
72-
73-
export function toDisposable(d: () => void): Disposable {
74-
return { dispose: d };
75-
}
76-
77-
export function combinedDisposable(disposables: Disposable[]): Disposable {
78-
return toDisposable(() => dispose(disposables));
79-
}
80-
8169
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
8270
return (listener, thisArgs = null, disposables?) => {
8371
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));

src/common/webview.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import * as vscode from 'vscode';
77
import { commands } from './executeCommands';
8+
import { Disposable } from './lifecycle';
89

910
export const PULL_REQUEST_OVERVIEW_VIEW_TYPE = 'PullRequestOverview';
1011

@@ -29,16 +30,16 @@ export function getNonce() {
2930
return text;
3031
}
3132

32-
export class WebviewBase {
33+
export class WebviewBase extends Disposable {
3334
protected _webview?: vscode.Webview;
34-
protected _disposables: vscode.Disposable[] = [];
3535

3636
private _waitForReady: Promise<void>;
37-
private _onIsReady: vscode.EventEmitter<void> = new vscode.EventEmitter();
37+
private _onIsReady: vscode.EventEmitter<void> = this._register(new vscode.EventEmitter());
3838

3939
protected readonly MESSAGE_UNHANDLED: string = 'message not handled';
4040

4141
constructor() {
42+
super();
4243
this._waitForReady = new Promise(resolve => {
4344
const disposable = this._onIsReady.event(() => {
4445
disposable.dispose();
@@ -51,12 +52,9 @@ export class WebviewBase {
5152
const disposable = this._webview?.onDidReceiveMessage(
5253
async message => {
5354
await this._onDidReceiveMessage(message as IRequestMessage<any>);
54-
},
55-
null,
56-
this._disposables,
57-
);
55+
});
5856
if (disposable) {
59-
this._disposables.push(disposable);
57+
this._register(disposable);
6058
}
6159
}
6260

@@ -94,10 +92,6 @@ export class WebviewBase {
9492
};
9593
this._webview?.postMessage(reply);
9694
}
97-
98-
public dispose() {
99-
this._disposables.forEach(d => d.dispose());
100-
}
10195
}
10296

10397
export class WebviewViewBase extends WebviewBase {
@@ -122,7 +116,7 @@ export class WebviewViewBase extends WebviewBase {
122116

123117
localResourceRoots: [this._extensionUri],
124118
};
125-
this._disposables.push(this._view.onDidDispose(() => {
119+
this._register(this._view.onDidDispose(() => {
126120
this._webview = undefined;
127121
this._view = undefined;
128122
}));

src/experimentationService.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@ import {
1111
IExperimentationTelemetry,
1212
TargetPopulation,
1313
} from 'vscode-tas-client';
14+
import { Disposable } from './common/lifecycle';
1415

1516
/* __GDPR__
1617
"query-expfeature" : {
1718
"ABExp.queriedFeature": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
1819
}
1920
*/
2021

21-
export class ExperimentationTelemetry implements IExperimentationTelemetry {
22+
export class ExperimentationTelemetry extends Disposable implements IExperimentationTelemetry {
2223
private sharedProperties: Record<string, string> = {};
2324

24-
constructor(private baseReporter: TelemetryReporter | undefined) { }
25+
constructor(private baseReporter: TelemetryReporter | undefined) {
26+
super();
27+
if (baseReporter) {
28+
this._register(baseReporter);
29+
}
30+
}
2531

2632
sendTelemetryEvent(eventName: string, properties?: Record<string, string>, measurements?: Record<string, number>) {
2733
this.baseReporter?.sendTelemetryEvent(
@@ -56,10 +62,6 @@ export class ExperimentationTelemetry implements IExperimentationTelemetry {
5662
}
5763
this.sendTelemetryEvent(eventName, event);
5864
}
59-
60-
async dispose(): Promise<any> {
61-
return this.baseReporter?.dispose();
62-
}
6365
}
6466

6567
function getTargetPopulation(): TargetPopulation {

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async function init(
147147
}
148148
});
149149

150-
const changesTree = new PullRequestChangesTreeDataProvider(context, git, reposManager);
150+
const changesTree = new PullRequestChangesTreeDataProvider(git, reposManager);
151151
context.subscriptions.push(changesTree);
152152

153153
const activePrViewCoordinator = new WebviewViewCoordinator(context);

0 commit comments

Comments
 (0)