diff --git a/package.json b/package.json index 246184a0..0c69ef0b 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,16 @@ } }, "activationEvents": [ + "onLanguage:java", + "workspaceContains:pom.xml", + "workspaceContains:build.gradle", + "workspaceContains:settings.gradle", + "workspaceContains:build.gradle.kts", + "workspaceContains:settings.gradle.kts", + "workspaceContains:.classpath", "onCommand:_java.project.open", "onCommand:java.project.create", - "onCommand:java.view.package.exportJar", - "onCommand:java.view.package.revealInProjectExplorer", - "onCommand:java.view.package.newJavaClass", - "onView:javaProjectExplorer" + "onCommand:java.view.package.newJavaClass" ], "license": "MIT", "main": "./main.js", @@ -266,7 +270,7 @@ "commandPalette": [ { "command": "java.view.package.exportJar", - "when": "java:serverMode == Standard" + "when": "java:serverMode == Standard && !java:noJavaProjects" }, { "command": "java.view.package.refresh", @@ -348,7 +352,7 @@ "explorer/context": [ { "command": "java.view.package.revealInProjectExplorer", - "when": "resourceFilename in java:supportedBuildFiles && java:serverMode == Standard", + "when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard", "group": "navigation@100" }, { @@ -360,7 +364,7 @@ "editor/title/context": [ { "command": "java.view.package.revealInProjectExplorer", - "when": "resourceFilename in java:supportedBuildFiles && java:serverMode == Standard", + "when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard", "group": "2_files@100" }, { @@ -372,7 +376,7 @@ "view/title": [ { "command": "java.project.create", - "when": "view == javaProjectExplorer && java:serverMode == Standard", + "when": "view == javaProjectExplorer", "group": "navigation@10" }, { @@ -515,9 +519,9 @@ { "id": "javaProjectExplorer", "name": "Java Projects", - "when": "java:serverMode", "contextualTitle": "Java Projects", - "icon": "$(project)" + "icon": "$(project)", + "when": "resourceLangId == java || java:workspaceContainsBuildFiles || java:serverMode" } ] }, @@ -536,6 +540,11 @@ "view": "javaProjectExplorer", "contents": "%viewsWelcome.workbench.inLightWeightMode%", "when": "java:serverMode == LightWeight" + }, + { + "view": "javaProjectExplorer", + "contents": "%viewsWelcome.workbench.installLanguageSupport%", + "when": "java:projectManagerActivated && !java:languageSupportInstalled" } ], "taskDefinitions": [ diff --git a/package.nls.json b/package.nls.json index 44fe7bfe..5331478e 100644 --- a/package.nls.json +++ b/package.nls.json @@ -38,6 +38,7 @@ "taskDefinitions.java.project.exportJar.dependencies": "The artifact dependencies in the runtime scope.", "taskDefinitions.java.project.exportJar.testDependencies": "The artifact dependencies in the test scope.", "viewsWelcome.workbench.createNewJavaProject": "You can also [open a Java project](command:_java.project.open), or create a new Java project by clicking the button below.\n[Create Java Project](command:java.project.create)", - "viewsWelcome.workbench.noJavaProject": "No Java projects found in the current workspace. You can [open a Java project](command:_java.project.open), or create a new Java project by clicking the button below.\n[Create Java Project](command:java.project.create)", - "viewsWelcome.workbench.inLightWeightMode": "No projects are listed because the Java Language Server is currently running in [LightWeight Mode](https://aka.ms/vscode-java-lightweight). To show projects, click on the button to switch to Standard Mode.\n[Switch to Standard Mode](command:java.server.mode.switch?%5B%22Standard%22,true%5D)" + "viewsWelcome.workbench.noJavaProject": "No Java projects found in the current workspace. You can [open a Java project folder](command:_java.project.open), or create a new Java project by clicking the button below.\n[Create Java Project](command:java.project.create)", + "viewsWelcome.workbench.inLightWeightMode": "To view the projects, you can import the projects into workspace.\n[Import Projects](command:java.server.mode.switch?%5B%22Standard%22,true%5D)", + "viewsWelcome.workbench.installLanguageSupport": "The Java Projects explorer requires [Language Support for Java(TM) by Red Hat](command:extension.open?%5B%22redhat.java%22%5D) to provide full features.\n[Install](command:workbench.extensions.installExtension?%5B%22redhat.java%22%5D)" } diff --git a/package.nls.zh.json b/package.nls.zh.json index 3314e85b..054d88d5 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -38,6 +38,7 @@ "taskDefinitions.java.project.exportJar.dependencies": "在 runtime scope 内的依赖。", "taskDefinitions.java.project.exportJar.testDependencies": "在 test scope 内的依赖。", "viewsWelcome.workbench.createNewJavaProject": "您也可以[打开一个 Java 项目](command:_java.project.open),或点击下方按钮创建一个新的 Java 项目。\n[创建 Java 项目](command:java.project.create)", - "viewsWelcome.workbench.noJavaProject": "当前工作空间未发现 Java 项目,您可以[打开一个 Java 项目](command:_java.project.open),或点击下方按钮创建一个新的 Java 项目。\n[创建 Java 项目](command:java.project.create)", - "viewsWelcome.workbench.inLightWeightMode": "由于 Java 语言服务正运行在 [LightWeight 模式](https://aka.ms/vscode-java-lightweight)下,因此项目将不会展示在该视图中。如果您需要展示项目信息,可以点击下方按钮将 Java 语言服务切换至 Standard 模式。\n[切换至 Standard 模式](command:java.server.mode.switch?%5B%22Standard%22,true%5D)" + "viewsWelcome.workbench.noJavaProject": "当前工作空间未发现 Java 项目,您可以[打开一个 Java 项目目录](command:_java.project.open),或点击下方按钮创建一个新的 Java 项目。\n[创建 Java 项目](command:java.project.create)", + "viewsWelcome.workbench.inLightWeightMode": "要浏览项目信息,你可以将项目导入到工作空间中。\n[导入项目](command:java.server.mode.switch?%5B%22Standard%22,true%5D)", + "viewsWelcome.workbench.installLanguageSupport": "Java 项目视图需要安装并激活 [Language Support for Java(TM) by Red Hat](command:extension.open?%5B%22redhat.java%22%5D) 以提供完整的功能。\n[安装](command:workbench.extensions.installExtension?%5B%22redhat.java%22%5D)" } diff --git a/src/constants.ts b/src/constants.ts index 87eaca8e..e71cad32 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,8 +3,9 @@ export namespace Context { export const EXTENSION_ACTIVATED: string = "java:projectManagerActivated"; - export const SUPPORTED_BUILD_FILES: string = "java:supportedBuildFiles"; - export const NO_JAVA_PEOJECT: string = "java:noJavaProjects"; + export const LANGUAGE_SUPPORT_INSTALLED: string = "java:languageSupportInstalled"; + export const NO_JAVA_PROJECT: string = "java:noJavaProjects"; + export const WORKSPACE_CONTAINS_BUILD_FILES: string = "java:workspaceContainsBuildFiles"; } export namespace Explorer { @@ -21,10 +22,6 @@ export namespace Explorer { } } -export namespace Build { - export const FILE_NAMES: string[] = ["pom.xml", "build.gradle"]; -} - export namespace ExtensionName { export const JAVA_LANGUAGE_SUPPORT: string = "redhat.java"; } diff --git a/src/exportJarSteps/ExportJarTaskProvider.ts b/src/exportJarSteps/ExportJarTaskProvider.ts index 0c078b0e..6d3aadf6 100644 --- a/src/exportJarSteps/ExportJarTaskProvider.ts +++ b/src/exportJarSteps/ExportJarTaskProvider.ts @@ -40,7 +40,7 @@ export async function executeExportJarTask(node?: INodeData): Promise { // save the workspace first await workspace.saveAll(false /*includeUntitled*/); - if (!await languageServerApiManager.isStandardServerReady() || isExportingJar || await buildWorkspace() === false) { + if (!await languageServerApiManager.ready() || isExportingJar || await buildWorkspace() === false) { return; } isExportingJar = true; diff --git a/src/extension.ts b/src/extension.ts index 99043868..e4fad0cb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { Event, Extension, ExtensionContext, extensions, tasks, Uri } from "vscode"; +import { ExtensionContext, tasks, Uri, workspace } from "vscode"; import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation, sendInfo } from "vscode-extension-telemetry-wrapper"; import { contextManager } from "../extension.bundle"; -import { Build, Context, ExtensionName } from "./constants"; +import { Context } from "./constants"; import { LibraryController } from "./controllers/libraryController"; import { ProjectController } from "./controllers/projectController"; import { init as initExpService } from "./ExperimentationService"; import { ExportJarTaskProvider } from "./exportJarSteps/ExportJarTaskProvider"; +import { languageServerApiManager } from "./languageServerApi/languageServerApiManager"; import { Settings } from "./settings"; import { syncHandler } from "./syncHandler"; import { EventCounter } from "./utility"; @@ -19,11 +20,19 @@ export async function activate(context: ExtensionContext): Promise { await initializeFromJsonFile(context.asAbsolutePath("./package.json"), { firstParty: true }); await initExpService(context); await instrumentOperation("activation", activateExtension)(context); + languageServerApiManager.initializeJavaLanguageServerApi(false); + // the when clause does not support 'workspaceContains' we used for activation event, + // so we manually find the target files and set it to a context value. + workspace.findFiles("{*.gradle,*.gradle.kts,pom.xml,.classpath}", undefined, 1).then((uris: Uri[]) => { + if (uris && uris.length) { + contextManager.setContextValue(Context.WORKSPACE_CONTAINS_BUILD_FILES, true); + } + }); contextManager.setContextValue(Context.EXTENSION_ACTIVATED, true); - contextManager.setContextValue(Context.SUPPORTED_BUILD_FILES, Build.FILE_NAMES); } async function activateExtension(_operationId: string, context: ExtensionContext): Promise { + context.subscriptions.push(languageServerApiManager); context.subscriptions.push(new ProjectController(context)); Settings.initialize(context); context.subscriptions.push(new LibraryController(context)); @@ -31,41 +40,6 @@ async function activateExtension(_operationId: string, context: ExtensionContext context.subscriptions.push(contextManager); context.subscriptions.push(syncHandler); context.subscriptions.push(tasks.registerTaskProvider(ExportJarTaskProvider.exportJarType, new ExportJarTaskProvider())); - - const pollingJLS = () => { - const javaLanguageSupport: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_LANGUAGE_SUPPORT); - if (!javaLanguageSupport) { - return; - } - - if (javaLanguageSupport.isActive) { - const extensionApi: any = javaLanguageSupport.exports; - if (!extensionApi) { - return; - } - - if (extensionApi.onDidClasspathUpdate) { - const onDidClasspathUpdate: Event = extensionApi.onDidClasspathUpdate; - context.subscriptions.push(onDidClasspathUpdate(async () => { - syncHandler.updateFileWatcher(Settings.autoRefresh()); - })); - } - - if (extensionApi.serverMode === "Standard") { - syncHandler.updateFileWatcher(Settings.autoRefresh()); - } else { - if (extensionApi.onDidServerModeChange) { - const onDidServerModeChange: Event = extensionApi.onDidServerModeChange; - context.subscriptions.push(onDidServerModeChange(async () => { - syncHandler.updateFileWatcher(Settings.autoRefresh()); - })); - } - } - } else { - setTimeout(pollingJLS, 3 * 1000 /*ms*/); - } - }; - pollingJLS(); } // this method is called when your extension is deactivated diff --git a/src/languageServerApi/languageServerApiManager.ts b/src/languageServerApi/languageServerApiManager.ts index 8837a22f..3f207848 100644 --- a/src/languageServerApi/languageServerApiManager.ts +++ b/src/languageServerApi/languageServerApiManager.ts @@ -1,54 +1,58 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { commands, Event, Extension, extensions, Uri } from "vscode"; +import { commands, Disposable, Event, Extension, extensions, Uri } from "vscode"; import { Commands } from "../commands"; +import { Context, ExtensionName } from "../constants"; import { contextManager } from "../contextManager"; +import { Settings } from "../settings"; +import { syncHandler } from "../syncHandler"; import { LanguageServerMode } from "./LanguageServerMode"; -class LanguageServerApiManager { +class LanguageServerApiManager implements Disposable { + /** + * undefined means a legacy version language server + * null means the JDT.LS is not activated + */ private serverMode: LanguageServerMode | null | undefined = null; - public async isStandardServerReady(): Promise { + private extensionChangeListener: Disposable; + + public async ready(): Promise { await this.checkServerMode(); - // undefined serverMode indicates an older version language server - if (this.serverMode === undefined) { - return true; - } - if (this.serverMode !== LanguageServerMode.Standard) { + if (this.serverMode === null || this.serverMode === LanguageServerMode.LightWeight) { return false; } - return true; - } - - public async isLightWeightMode(): Promise { - await this.checkServerMode(); - return this.serverMode === LanguageServerMode.LightWeight; - } - - public async awaitSwitchingServerFinished(): Promise { - await this.checkServerMode(); if (this.serverMode === LanguageServerMode.Hybrid) { await new Promise((resolve: () => void): void => { extensions.getExtension("redhat.java")!.exports.onDidServerModeChange(resolve); }); } - } - private async checkServerMode(): Promise { - if (this.serverMode === null) { - await this.initializeJavaLanguageServerApi(); - } + return true; } - private async initializeJavaLanguageServerApi(): Promise { - if (this.serverMode !== null) { + public async initializeJavaLanguageServerApi(forceActivate: boolean = true): Promise { + if (this.isLanguageServerActivated()) { return; } - const extension: Extension | undefined = extensions.getExtension("redhat.java"); + + if (!this.extensionChangeListener) { + this.extensionChangeListener = extensions.onDidChange(() => { + if (this.serverMode === null) { + commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */false); + } + }); + } + + const extension: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_LANGUAGE_SUPPORT); if (extension) { + contextManager.setContextValue(Context.LANGUAGE_SUPPORT_INSTALLED, true); + if (!forceActivate) { + return; + } await extension.activate(); const extensionApi: any = extension.exports; if (!extensionApi) { @@ -56,39 +60,55 @@ class LanguageServerApiManager { } this.serverMode = extensionApi.serverMode; + if (this.serverMode === LanguageServerMode.Standard) { + syncHandler.updateFileWatcher(Settings.autoRefresh()); + } if (extensionApi.onDidClasspathUpdate) { const onDidClasspathUpdate: Event = extensionApi.onDidClasspathUpdate; - contextManager.context.subscriptions.push(onDidClasspathUpdate(async () => { - await commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */true); + contextManager.context.subscriptions.push(onDidClasspathUpdate(() => { + commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */true); + syncHandler.updateFileWatcher(Settings.autoRefresh()); })); } if (extensionApi.onDidServerModeChange) { const onDidServerModeChange: Event = extensionApi.onDidServerModeChange; - contextManager.context.subscriptions.push(onDidServerModeChange(async (mode: LanguageServerMode) => { + contextManager.context.subscriptions.push(onDidServerModeChange((mode: LanguageServerMode) => { 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) { + if (mode === LanguageServerMode.Hybrid) { commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */false); + } else if (mode === LanguageServerMode.Standard) { + syncHandler.updateFileWatcher(Settings.autoRefresh()); } + this.serverMode = mode; } })); } if (extensionApi.onDidProjectsImport) { const onDidProjectsImport: Event = extensionApi.onDidProjectsImport; - contextManager.context.subscriptions.push(onDidProjectsImport(async () => { + contextManager.context.subscriptions.push(onDidProjectsImport(() => { commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH, /* debounce = */true); + syncHandler.updateFileWatcher(Settings.autoRefresh()); })); } } } + + public dispose() { + this.extensionChangeListener.dispose(); + } + + private isLanguageServerActivated(): boolean { + return this.serverMode !== null; + } + + private async checkServerMode(): Promise { + if (!this.isLanguageServerActivated()) { + await this.initializeJavaLanguageServerApi(); + } + } } export const languageServerApiManager: LanguageServerApiManager = new LanguageServerApiManager(); diff --git a/src/utility.ts b/src/utility.ts index b0e368ff..ee647dca 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -31,13 +31,8 @@ export class Utility { return false; } - await languageServerApiManager.awaitSwitchingServerFinished(); - if (!await languageServerApiManager.isStandardServerReady()) { - return false; - } - return true; + return languageServerApiManager.ready(); } - } export class EventCounter { diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index d0891f0f..7901497c 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -97,12 +97,10 @@ export class DependencyDataProvider implements TreeDataProvider { } public async getChildren(element?: ExplorerNode): Promise { - if (await languageServerApiManager.isLightWeightMode()) { + if (!await languageServerApiManager.ready()) { return []; } - await languageServerApiManager.awaitSwitchingServerFinished(); - const children = (!this._rootItems || !element) ? await this.getRootNodes() : await element.getChildren(); @@ -172,7 +170,7 @@ export class DependencyDataProvider implements TreeDataProvider { this._rootItems = rootItems; } } - contextManager.setContextValue(Context.NO_JAVA_PEOJECT, _.isEmpty(rootItems)); + contextManager.setContextValue(Context.NO_JAVA_PROJECT, _.isEmpty(rootItems)); return rootItems; } finally { explorerLock.release(); diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index f9c1f155..d5f99d12 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -10,7 +10,6 @@ import { } from "vscode"; import { instrumentOperationAsVsCodeCommand, sendInfo } from "vscode-extension-telemetry-wrapper"; import { Commands } from "../commands"; -import { Build } from "../constants"; import { deleteFiles } from "../explorerCommands/delete"; import { newJavaClass, newPackage } from "../explorerCommands/new"; import { renameFile } from "../explorerCommands/rename"; @@ -68,10 +67,9 @@ export class DependencyExplorer implements Disposable { await commands.executeCommand(Commands.JAVA_PROJECT_EXPLORER_FOCUS); let fsPath: string = uri.fsPath; const fileName: string = path.basename(fsPath); - if (Build.FILE_NAMES.includes(fileName)) { + if (/(.*\.gradle)|(.*\.gradle\.kts)|(pom\.xml)$/.test(fileName)) { fsPath = path.dirname(fsPath); } - uri = Uri.file(fsPath); if ((await fse.stat(fsPath)).isFile()) { await commands.executeCommand(Commands.VSCODE_OPEN, uri, { preserveFocus: true });