Skip to content

Commit 6a22293

Browse files
authored
bugfix: Fix the bug that the explorer will be out-of-sync occasionally (#340)
1 parent a324d98 commit 6a22293

File tree

5 files changed

+86
-27
lines changed

5 files changed

+86
-27
lines changed

src/syncHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class SyncHandler implements Disposable {
8787
} else {
8888
// the direct parent is not rendered in the explorer, the returned node
8989
// is other package fragment, we need to refresh the package fragment root.
90-
while (node && node.nodeData.kind !== NodeKind.PackageRoot) {
90+
while (node && node.nodeData.kind > NodeKind.PackageRoot) {
9191
node = <DataNode>node.getParent();
9292
}
9393
return node;

src/utils/Lock.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { EventEmitter } from "events";
5+
6+
export class Lock {
7+
private _locked: boolean;
8+
private _eventEmitter: EventEmitter;
9+
10+
constructor() {
11+
this._locked = false;
12+
this._eventEmitter = new EventEmitter();
13+
}
14+
15+
public async acquire(): Promise<void> {
16+
return new Promise((resolve) => {
17+
if (!this._locked) {
18+
this._locked = true;
19+
return resolve();
20+
}
21+
22+
const tryAcquire = () => {
23+
if (!this._locked) {
24+
this._locked = true;
25+
this._eventEmitter.removeListener("release", tryAcquire);
26+
return resolve();
27+
}
28+
};
29+
this._eventEmitter.on("release", tryAcquire);
30+
});
31+
}
32+
33+
public release(): void {
34+
this._locked = false;
35+
setImmediate(() => this._eventEmitter.emit("release"));
36+
}
37+
}

src/views/dataNode.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
// Licensed under the MIT license.
33

44
import * as _ from "lodash";
5-
import { ProviderResult, ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
5+
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
66
import { INodeData } from "../java/nodeData";
7+
import { Lock } from "../utils/Lock";
78
import { ExplorerNode } from "./explorerNode";
89

910
export abstract class DataNode extends ExplorerNode {
11+
12+
protected _lock: Lock = new Lock();
13+
1014
constructor(protected _nodeData: INodeData, parent: DataNode) {
1115
super(parent);
1216
}
@@ -56,14 +60,18 @@ export abstract class DataNode extends ExplorerNode {
5660
return (childNode && paths.length) ? childNode.revealPaths(paths) : childNode;
5761
}
5862

59-
public getChildren(): ProviderResult<ExplorerNode[]> {
60-
if (!this._nodeData.children) {
61-
return this.loadData().then((res) => {
62-
this._nodeData.children = res;
63+
public async getChildren(): Promise<ExplorerNode[]> {
64+
try {
65+
await this._lock.acquire();
66+
if (!this._nodeData.children) {
67+
const data = await this.loadData();
68+
this._nodeData.children = data;
6369
return this.createChildNodeList();
64-
});
70+
}
71+
return this.createChildNodeList();
72+
} finally {
73+
this._lock.release();
6574
}
66-
return this.createChildNodeList();
6775
}
6876

6977
protected computeContextValue(): string {

src/views/dependencyDataProvider.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { isLightWeightMode, isSwitchingServer } from "../extension";
1414
import { Jdtls } from "../java/jdtls";
1515
import { INodeData, NodeKind } from "../java/nodeData";
1616
import { Settings } from "../settings";
17+
import { Lock } from "../utils/Lock";
1718
import { DataNode } from "./dataNode";
1819
import { ExplorerNode } from "./explorerNode";
1920
import { explorerNodeCache } from "./nodeCache/explorerNodeCache";
@@ -24,6 +25,8 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
2425

2526
private _onDidChangeTreeData: EventEmitter<ExplorerNode | null | undefined> = new EventEmitter<ExplorerNode | null | undefined>();
2627

28+
private _lock: Lock = new Lock();
29+
2730
// tslint:disable-next-line:member-ordering
2831
public onDidChangeTreeData: Event<ExplorerNode | null | undefined> = this._onDidChangeTreeData.event;
2932

@@ -125,7 +128,7 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
125128
}
126129

127130
private async getRootProjects(): Promise<ExplorerNode[]> {
128-
const rootElements = this._rootItems ? this._rootItems : await this.getChildren();
131+
const rootElements = await this.getRootNodes();
129132
if (rootElements[0] instanceof ProjectNode) {
130133
return rootElements;
131134
} else {
@@ -138,9 +141,15 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
138141
}
139142
}
140143

141-
private getRootNodes(): Thenable<ExplorerNode[]> {
142-
return new Promise((resolve, reject) => {
143-
const rootItems = new Array<ExplorerNode>();
144+
private async getRootNodes(): Promise<ExplorerNode[]> {
145+
try {
146+
await this._lock.acquire();
147+
148+
if (this._rootItems) {
149+
return this._rootItems;
150+
}
151+
152+
const rootItems: ExplorerNode[] = [];
144153
const folders = workspace.workspaceFolders;
145154
if (folders && folders.length) {
146155
if (folders.length > 1) {
@@ -150,19 +159,20 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
150159
kind: NodeKind.Workspace,
151160
}, null)));
152161
this._rootItems = rootItems;
153-
resolve(rootItems);
162+
return rootItems;
154163
} else {
155-
Jdtls.getProjects(folders[0].uri.toString()).then((result: INodeData[]) => {
156-
result.forEach((project) => {
157-
rootItems.push(new ProjectNode(project, null));
158-
});
159-
this._rootItems = rootItems;
160-
resolve(rootItems);
164+
const result: INodeData[] = await Jdtls.getProjects(folders[0].uri.toString());
165+
result.forEach((project) => {
166+
rootItems.push(new ProjectNode(project, null));
161167
});
168+
this._rootItems = rootItems;
169+
return rootItems;
162170
}
163171
} else {
164-
reject("No workspace found");
172+
throw new Error("No workspace folder found, please open a folder into the workspace first.");
165173
}
166-
});
174+
} finally {
175+
this._lock.release();
176+
}
167177
}
168178
}

src/views/hierarchicalPackageNode.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,21 @@ export class HierarchicalPackageNode extends PackageNode {
2525
}
2626
}
2727

28-
public getChildren(): ProviderResult<ExplorerNode[]> {
29-
return this.loadData().then((res) => {
30-
if (res) {
28+
public async getChildren(): Promise<ExplorerNode[]> {
29+
try {
30+
await this._lock.acquire();
31+
const data = await this.loadData();
32+
if (data) {
3133
if (this.nodeData?.children) {
32-
this.nodeData.children.push(...res);
34+
this.nodeData.children.push(...data);
3335
} else {
34-
this.nodeData.children = res;
36+
this.nodeData.children = data;
3537
}
3638
}
3739
return this.createChildNodeList();
38-
});
40+
} finally {
41+
this._lock.release();
42+
}
3943
}
4044

4145
public async revealPaths(paths: INodeData[]): Promise<DataNode> {

0 commit comments

Comments
 (0)