Skip to content

Commit 65c1972

Browse files
committed
Fix #136. Memory leak when tree view is refreshed.
1 parent ea39377 commit 65c1972

6 files changed

Lines changed: 58 additions & 17 deletions

File tree

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/view/prsTreeDataProvider.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class PullRequestsTreeDataProvider implements vscode.TreeDataProvider<Tre
1717
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
1818
get onDidChange(): vscode.Event<vscode.Uri> { return this._onDidChange.event; }
1919
private _disposables: vscode.Disposable[];
20+
private _childrenDisposables: vscode.Disposable[];
2021

2122
constructor(
2223
private _configuration: Configuration,
@@ -39,6 +40,7 @@ export class PullRequestsTreeDataProvider implements vscode.TreeDataProvider<Tre
3940
this._disposables.push(this._configuration.onDidChange(e => {
4041
this._onDidChangeTreeData.fire();
4142
}));
43+
this._childrenDisposables = [];
4244
}
4345

4446
getTreeItem(element: TreeNode): vscode.TreeItem {
@@ -47,13 +49,20 @@ export class PullRequestsTreeDataProvider implements vscode.TreeDataProvider<Tre
4749

4850
async getChildren(element?: TreeNode): Promise<TreeNode[]> {
4951
if (!element) {
50-
return Promise.resolve([
52+
if (this._childrenDisposables && this._childrenDisposables.length) {
53+
this._childrenDisposables.forEach(dispose => dispose.dispose());
54+
}
55+
56+
let result = [
5157
new CategoryTreeNode(this._prManager, this._repository, PRType.LocalPullRequest),
5258
new CategoryTreeNode(this._prManager, this._repository, PRType.RequestReview),
5359
new CategoryTreeNode(this._prManager, this._repository, PRType.AssignedToMe),
5460
new CategoryTreeNode(this._prManager, this._repository, PRType.Mine),
5561
new CategoryTreeNode(this._prManager, this._repository, PRType.All)
56-
]);
62+
];
63+
64+
this._childrenDisposables = result;
65+
return Promise.resolve(result);
5766
}
5867
if (!this._repository.remotes || !this._repository.remotes.length) {
5968
return Promise.resolve([new PRCategoryActionNode(PRCategoryActionType.Empty)]);

src/view/treeNodes/categoryNode.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,13 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
129129
nodes.push(new PRCategoryActionNode(PRCategoryActionType.More, this));
130130
}
131131

132+
this.childrenDisposables = nodes;
132133
return nodes;
133134
} else {
134-
return [new PRCategoryActionNode(PRCategoryActionType.Empty)];
135+
let result = [new PRCategoryActionNode(PRCategoryActionType.Empty)];
136+
137+
this.childrenDisposables = result;
138+
return result;
135139
}
136140
}
137141

src/view/treeNodes/fileChangeNode.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,4 @@ export class FileChangeNode extends TreeNode implements vscode.TreeItem {
7777
getTreeItem(): vscode.TreeItem {
7878
return this;
7979
}
80-
8180
}

src/view/treeNodes/pullRequestNode.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import { FileChangeNode } from './fileChangeNode';
2020
import { TreeNode } from './treeNode';
2121

2222
export class PRNode extends TreeNode {
23-
private richContentChanges: RichFileChange[];
24-
private commentsCache: Map<String, Comment[]>;
23+
private _richContentChanges: RichFileChange[];
24+
private _commentsCache: Map<String, Comment[]>;
25+
private _documentCommentsProvider: vscode.Disposable;
2526

2627
constructor(
2728
private _prManager: IPullRequestManager,
@@ -30,16 +31,25 @@ export class PRNode extends TreeNode {
3031
private _isLocal: boolean
3132
) {
3233
super();
34+
this._documentCommentsProvider = null;
3335
}
3436

3537
async getChildren(): Promise<TreeNode[]> {
3638
try {
39+
if (this._documentCommentsProvider) {
40+
this._documentCommentsProvider.dispose();
41+
}
42+
43+
if (this.childrenDisposables && this.childrenDisposables.length) {
44+
this.childrenDisposables.forEach(dp => dp.dispose());
45+
}
46+
3747
const comments = await this._prManager.getPullRequestComments(this.pullRequestModel);
3848
const data = await this._prManager.getPullRequestChangedFiles(this.pullRequestModel);
3949
await this._prManager.fullfillPullRequestCommitInfo(this.pullRequestModel);
40-
this.richContentChanges = await parseDiff(data, this.repository, this.pullRequestModel.base.sha);
41-
this.commentsCache = new Map<String, Comment[]>();
42-
let fileChanges = this.richContentChanges.map(change => {
50+
this._richContentChanges = await parseDiff(data, this.repository, this.pullRequestModel.base.sha);
51+
this._commentsCache = new Map<String, Comment[]>();
52+
let fileChanges = this._richContentChanges.map(change => {
4353
let fileInRepo = path.resolve(this.repository.path, change.fileName);
4454
let changedItem = new FileChangeNode(
4555
this.pullRequestModel,
@@ -51,22 +61,26 @@ export class PRNode extends TreeNode {
5161
change.diffHunks,
5262
comments.filter(comment => comment.path === change.fileName && comment.position !== null)
5363
);
54-
this.commentsCache.set(change.fileName, changedItem.comments);
64+
this._commentsCache.set(change.fileName, changedItem.comments);
5565
return changedItem;
5666
});
5767

5868
const _onDidChangeCommentThreads = new vscode.EventEmitter<vscode.CommentThreadChangedEvent>();
59-
vscode.workspace.registerDocumentCommentProvider({
69+
70+
this._documentCommentsProvider = vscode.workspace.registerDocumentCommentProvider({
6071
onDidChangeCommentThreads: _onDidChangeCommentThreads.event,
6172
provideDocumentComments: this.provideDocumentComments.bind(this),
6273
createNewCommentThread: this.createNewCommentThread.bind(this),
6374
replyToCommentThread: this.replyToCommentThread.bind(this)
6475
});
6576

66-
return [new DescriptionNode('Description', {
77+
let result = [new DescriptionNode('Description', {
6778
light: Resource.icons.light.Description,
6879
dark: Resource.icons.dark.Description
6980
}, this.pullRequestModel), ...fileChanges];
81+
82+
this.childrenDisposables = result;
83+
return result;
7084
} catch (e) {
7185
Logger.appendLine(e);
7286
}
@@ -88,7 +102,7 @@ export class PRNode extends TreeNode {
88102
let uri = document.uri;
89103
let params = JSON.parse(uri.query);
90104

91-
let fileChange = this.richContentChanges.find(change => change.fileName === params.fileName);
105+
let fileChange = this._richContentChanges.find(change => change.fileName === params.fileName);
92106

93107
if (!fileChange) {
94108
return null;
@@ -144,7 +158,7 @@ export class PRNode extends TreeNode {
144158
if (document.uri.scheme === 'pr') {
145159
let params = JSON.parse(document.uri.query);
146160
let isBase = params.base;
147-
let fileChange = this.richContentChanges.find(change => change.fileName === params.fileName);
161+
let fileChange = this._richContentChanges.find(change => change.fileName === params.fileName);
148162
if (!fileChange) {
149163
return null;
150164
}
@@ -168,7 +182,7 @@ export class PRNode extends TreeNode {
168182
commentingRanges.push(new vscode.Range(startingLine, 0, startingLine + length, 0));
169183
}
170184

171-
let matchingComments = this.commentsCache.get(fileChange.fileName);
185+
let matchingComments = this._commentsCache.get(fileChange.fileName);
172186

173187
if (!matchingComments || !matchingComments.length) {
174188
return {
@@ -218,4 +232,12 @@ export class PRNode extends TreeNode {
218232

219233
return null;
220234
}
235+
236+
dispose(): void {
237+
super.dispose();
238+
239+
if (this._documentCommentsProvider) {
240+
this._documentCommentsProvider.dispose();
241+
}
242+
}
221243
}

src/view/treeNodes/treeNode.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@
55

66
import * as vscode from 'vscode';
77

8-
export abstract class TreeNode {
8+
export abstract class TreeNode implements vscode.Disposable {
9+
childrenDisposables: vscode.Disposable[];
10+
911
constructor() { }
1012
abstract getTreeItem(): vscode.TreeItem;
1113

1214
async getChildren(): Promise<TreeNode[]> {
1315
return [];
1416
}
17+
dispose(): void {
18+
if (this.childrenDisposables && this.childrenDisposables) {
19+
this.childrenDisposables.forEach(dispose => dispose.dispose());
20+
}
21+
}
1522
}

0 commit comments

Comments
 (0)