diff --git a/package-lock.json b/package-lock.json index b6bc1d1d..b6952dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -868,6 +868,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "await-lock": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.1.0.tgz", + "integrity": "sha512-t7Zm5YGgEEc/3eYAicF32m/TNvL+XOeYZy9CvBUeJY/szM7frLolFylhrlZNWV/ohWhcUXygrBGjYmoQdxF4CQ==" + }, "axios": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", diff --git a/package.json b/package.json index 7683e430..5d98fc08 100644 --- a/package.json +++ b/package.json @@ -602,6 +602,7 @@ "webpack-cli": "^3.3.12" }, "dependencies": { + "await-lock": "^2.1.0", "fs-extra": "^7.0.1", "globby": "11.0.1", "lodash": "^4.17.21", diff --git a/src/languageServerApi/languageServerApiManager.ts b/src/languageServerApi/languageServerApiManager.ts index 90bc2fe5..8837a22f 100644 --- a/src/languageServerApi/languageServerApiManager.ts +++ b/src/languageServerApi/languageServerApiManager.ts @@ -28,9 +28,13 @@ class LanguageServerApiManager { return this.serverMode === LanguageServerMode.LightWeight; } - public async isSwitchingServer(): Promise { + public async awaitSwitchingServerFinished(): Promise { await this.checkServerMode(); - return this.serverMode === LanguageServerMode.Hybrid; + if (this.serverMode === LanguageServerMode.Hybrid) { + await new Promise((resolve: () => void): void => { + extensions.getExtension("redhat.java")!.exports.onDidServerModeChange(resolve); + }); + } } private async checkServerMode(): Promise { @@ -63,8 +67,17 @@ class LanguageServerApiManager { if (extensionApi.onDidServerModeChange) { const onDidServerModeChange: Event = extensionApi.onDidServerModeChange; contextManager.context.subscriptions.push(onDidServerModeChange(async (mode: LanguageServerMode) => { - this.serverMode = mode; - commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */false); + if (this.serverMode !== mode) { + let needRefresh: boolean = true; + if (this.serverMode === "Hybrid") { + // Explorer will await when JLS is in Hybrid mode (activating), + needRefresh = false; + } + this.serverMode = mode; + if (needRefresh) { + commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */false); + } + } })); } diff --git a/src/utility.ts b/src/utility.ts index e3202ab3..b0e368ff 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -30,6 +30,8 @@ export class Utility { if (uri.scheme === "file" && !workspace.getWorkspaceFolder(uri)) { return false; } + + await languageServerApiManager.awaitSwitchingServerFinished(); if (!await languageServerApiManager.isStandardServerReady()) { return false; } diff --git a/src/utils/Lock.ts b/src/utils/Lock.ts index 62c1691a..9869fa98 100644 --- a/src/utils/Lock.ts +++ b/src/utils/Lock.ts @@ -1,37 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { EventEmitter } from "events"; +import AwaitLock from "await-lock"; -export class Lock { - private _locked: boolean; - private _eventEmitter: EventEmitter; - - constructor() { - this._locked = false; - this._eventEmitter = new EventEmitter(); - } - - public async acquire(): Promise { - return new Promise((resolve) => { - if (!this._locked) { - this._locked = true; - return resolve(); - } - - const tryAcquire = () => { - if (!this._locked) { - this._locked = true; - this._eventEmitter.removeListener("release", tryAcquire); - return resolve(); - } - }; - this._eventEmitter.on("release", tryAcquire); - }); - } - - public release(): void { - this._locked = false; - setImmediate(() => this._eventEmitter.emit("release")); - } -} +export const explorerLock: AwaitLock = new AwaitLock(); diff --git a/src/views/dataNode.ts b/src/views/dataNode.ts index 54dbd5ef..699d7352 100644 --- a/src/views/dataNode.ts +++ b/src/views/dataNode.ts @@ -4,15 +4,13 @@ import * as _ from "lodash"; import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; import { INodeData, NodeKind } from "../java/nodeData"; -import { Lock } from "../utils/Lock"; +import { explorerLock } from "../utils/Lock"; import { ExplorerNode } from "./explorerNode"; export abstract class DataNode extends ExplorerNode { protected _childrenNodes: ExplorerNode[]; - protected _lock: Lock = new Lock(); - constructor(protected _nodeData: INodeData, parent?: DataNode) { super(parent); } @@ -74,7 +72,7 @@ export abstract class DataNode extends ExplorerNode { public async getChildren(): Promise { try { - await this._lock.acquire(); + await explorerLock.acquireAsync(); if (!this._nodeData.children) { const data = await this.loadData(); this._nodeData.children = data; @@ -83,7 +81,7 @@ export abstract class DataNode extends ExplorerNode { } return this._childrenNodes; } finally { - this._lock.release(); + explorerLock.release(); } } diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index aebc3c27..1afd19db 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -3,7 +3,7 @@ import * as _ from "lodash"; import { - commands, Event, EventEmitter, ExtensionContext, extensions, ProviderResult, + commands, Event, EventEmitter, ExtensionContext, ProviderResult, RelativePattern, TreeDataProvider, TreeItem, Uri, window, workspace, } from "vscode"; import { instrumentOperation, instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper"; @@ -15,7 +15,7 @@ import { Jdtls } from "../java/jdtls"; import { INodeData, NodeKind } from "../java/nodeData"; import { languageServerApiManager } from "../languageServerApi/languageServerApiManager"; import { Settings } from "../settings"; -import { Lock } from "../utils/Lock"; +import { explorerLock } from "../utils/Lock"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; import { explorerNodeCache } from "./nodeCache/explorerNodeCache"; @@ -26,8 +26,6 @@ export class DependencyDataProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter = new EventEmitter(); - private _lock: Lock = new Lock(); - // tslint:disable-next-line:member-ordering public onDidChangeTreeData: Event = this._onDidChangeTreeData.event; @@ -100,11 +98,7 @@ export class DependencyDataProvider implements TreeDataProvider { return []; } - if (await languageServerApiManager.isSwitchingServer()) { - await new Promise((resolve: () => void): void => { - extensions.getExtension("redhat.java")!.exports.onDidServerModeChange(resolve); - }); - } + await languageServerApiManager.awaitSwitchingServerFinished(); const children = (!this._rootItems || !element) ? await this.getRootNodes() : await element.getChildren(); @@ -151,7 +145,7 @@ export class DependencyDataProvider implements TreeDataProvider { private async getRootNodes(): Promise { try { - await this._lock.acquire(); + await explorerLock.acquireAsync(); if (this._rootItems) { return this._rootItems; @@ -178,7 +172,7 @@ export class DependencyDataProvider implements TreeDataProvider { contextManager.setContextValue(Context.NO_JAVA_PEOJECT, _.isEmpty(rootItems)); return rootItems; } finally { - this._lock.release(); + explorerLock.release(); } } } diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index 1adc5107..e28e4576 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -4,8 +4,10 @@ import * as fse from "fs-extra"; import * as _ from "lodash"; import * as path from "path"; -import { commands, Disposable, ExtensionContext, QuickPickItem, TextEditor, TreeView, - TreeViewExpansionEvent, TreeViewSelectionChangeEvent, TreeViewVisibilityChangeEvent, Uri, window } from "vscode"; +import { + commands, Disposable, ExtensionContext, QuickPickItem, TextEditor, TreeView, + TreeViewExpansionEvent, TreeViewSelectionChangeEvent, TreeViewVisibilityChangeEvent, Uri, window, +} from "vscode"; import { instrumentOperationAsVsCodeCommand, sendInfo } from "vscode-extension-telemetry-wrapper"; import { Commands } from "../commands"; import { Build } from "../constants"; @@ -17,7 +19,6 @@ import { Jdtls } from "../java/jdtls"; import { INodeData } from "../java/nodeData"; import { Settings } from "../settings"; import { EventCounter, Utility } from "../utility"; -import { Lock } from "../utils/Lock"; import { DataNode } from "./dataNode"; import { DependencyDataProvider } from "./dependencyDataProvider"; import { ExplorerNode } from "./explorerNode"; @@ -34,8 +35,6 @@ export class DependencyExplorer implements Disposable { private static _instance: DependencyExplorer; - private _lock: Lock = new Lock(); - private _dependencyViewer: TreeView; private _dataProvider: DependencyDataProvider; @@ -54,7 +53,7 @@ export class DependencyExplorer implements Disposable { }), this._dependencyViewer.onDidChangeVisibility((e: TreeViewVisibilityChangeEvent) => { if (e.visible) { - sendInfo("", {projectManagerVisible: 1}); + sendInfo("", { projectManagerVisible: 1 }); if (window.activeTextEditor) { this.reveal(window.activeTextEditor.document.uri); } @@ -145,33 +144,27 @@ export class DependencyExplorer implements Disposable { } public async reveal(uri: Uri, needCheckSyncSetting: boolean = true): Promise { - try { - await this._lock.acquire(); - - if (needCheckSyncSetting && !Settings.syncWithFolderExplorer()) { - return; - } - - if (!await Utility.isRevealable(uri)) { - return; - } + if (needCheckSyncSetting && !Settings.syncWithFolderExplorer()) { + return; + } - let node: DataNode | undefined = explorerNodeCache.getDataNode(uri); - if (!node) { - const paths: INodeData[] = await Jdtls.resolvePath(uri.toString()); - if (!_.isEmpty(paths)) { - node = await this._dataProvider.revealPaths(paths); - } - } + if (!await Utility.isRevealable(uri)) { + return; + } - if (!node) { - return; + let node: DataNode | undefined = explorerNodeCache.getDataNode(uri); + if (!node) { + const paths: INodeData[] = await Jdtls.resolvePath(uri.toString()); + if (!_.isEmpty(paths)) { + node = await this._dataProvider.revealPaths(paths); } + } - await this._dependencyViewer.reveal(node); - } finally { - this._lock.release(); + if (!node) { + return; } + + await this._dependencyViewer.reveal(node); } public get dataProvider(): DependencyDataProvider { diff --git a/src/views/hierarchicalPackageNode.ts b/src/views/hierarchicalPackageNode.ts index e187999c..3364f7ff 100644 --- a/src/views/hierarchicalPackageNode.ts +++ b/src/views/hierarchicalPackageNode.ts @@ -5,6 +5,7 @@ import * as _ from "lodash"; import { TreeItem, TreeItemCollapsibleState } from "vscode"; import { HierarchicalPackageNodeData } from "../java/hierarchicalPackageNodeData"; import { INodeData, NodeKind } from "../java/nodeData"; +import { explorerLock } from "../utils/Lock"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; import { FileNode } from "./fileNode"; @@ -26,7 +27,7 @@ export class HierarchicalPackageNode extends PackageNode { public async getChildren(): Promise { try { - await this._lock.acquire(); + await explorerLock.acquireAsync(); const data = await this.loadData(); if (data) { if (this.nodeData?.children) { @@ -38,7 +39,7 @@ export class HierarchicalPackageNode extends PackageNode { } return this.createChildNodeList(); } finally { - this._lock.release(); + explorerLock.release(); } }