diff --git a/package-lock.json b/package-lock.json index d406269216..173ea5fbf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -288,9 +288,9 @@ "dev": true }, "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==", + "version": "16.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", + "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "dev": true }, "@types/semver": { @@ -6973,9 +6973,9 @@ "dev": true }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true }, "unbzip2-stream": { @@ -7254,20 +7254,41 @@ "vinyl": "^2.0.0" } }, + "vscode-jsonrpc": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==" + }, "vscode-languageclient": { - "version": "7.1.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.1.0-next.5.tgz", - "integrity": "sha512-TpzpAhpdCNJHaLzptFRs54xsU6xTmaiVPCse0W0rRB5jJBPjOBKilrFPMMm/sJA0y8Yxa9sOvZaNu6WPg3dYAw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", + "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", "requires": { - "minimatch": "^3.0.4", - "semver": "^7.3.4", - "vscode-languageserver-protocol": "3.17.0-next.6" + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.3" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } @@ -7275,25 +7296,18 @@ } }, "vscode-languageserver-protocol": { - "version": "3.17.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.6.tgz", - "integrity": "sha512-f1kGsoOpISB5jSqQNeMDl2446enxVahyux2e5vZap6pu/TC+2UlvPT4DCR0gPph95KOQZweL9zq1SzLoPdqhuA==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", + "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "requires": { - "vscode-jsonrpc": "7.0.0-next.1", - "vscode-languageserver-types": "3.17.0-next.2" - }, - "dependencies": { - "vscode-jsonrpc": { - "version": "7.0.0-next.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-7.0.0-next.1.tgz", - "integrity": "sha512-dEmliPZGbSyIcEeKRGzosCy7y7zsc8FXg1l5BBOGgMUbemlo3vUnsa2GFqpILJwJvlbvkRcF2QASNwIlKe9J7g==" - } + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" } }, "vscode-languageserver-types": { - "version": "3.17.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.2.tgz", - "integrity": "sha512-L5S2kNLCgYJMVWgsZjBaorMM/6+itAfvOyl6Kv1bgFzDNaUKm9HsnUlehjpWPdV5DqnfJhJ5E03Z+/3Mw8ii+Q==" + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", + "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "watchpack": { "version": "2.1.1", diff --git a/package.json b/package.json index 43a56009a7..e70da57df3 100644 --- a/package.json +++ b/package.json @@ -1397,7 +1397,7 @@ "@types/glob": "5.0.30", "@types/lodash.findindex": "^4.6.6", "@types/mocha": "^5.2.5", - "@types/node": "^8.10.51", + "@types/node": "^16.11.7", "@types/semver": "^7.3.8", "@types/sinon": "^10.0.12", "@types/vscode": "^1.74.0", @@ -1418,7 +1418,7 @@ "request": "^2.88.2", "sinon": "^14.0.0", "ts-loader": "^9.2.6", - "typescript": "^4.2.4", + "typescript": "^4.6.4", "webpack": "^5.28.0", "webpack-cli": "^4.6.0" }, @@ -1431,7 +1431,7 @@ "htmlparser2": "6.0.1", "jdk-utils": "^0.4.4", "semver": "^7.3.5", - "vscode-languageclient": "7.1.0-next.5", + "vscode-languageclient": "8.1.0", "winreg-utf8": "^0.1.1", "winston": "^3.2.1", "winston-daily-rotate-file": "^3.10.0" diff --git a/src/clientErrorHandler.ts b/src/clientErrorHandler.ts index 2e3c2d1850..ff7f551180 100644 --- a/src/clientErrorHandler.ts +++ b/src/clientErrorHandler.ts @@ -1,5 +1,5 @@ import { window, commands } from "vscode"; -import { ErrorHandler, Message, ErrorAction, CloseAction } from "vscode-languageclient"; +import { ErrorHandler, Message, ErrorAction, CloseAction, ErrorHandlerResult, CloseHandlerResult } from "vscode-languageclient"; import { Commands } from "./commands"; import { logger } from "./log"; @@ -10,21 +10,27 @@ export class ClientErrorHandler implements ErrorHandler { this.restarts = []; } - public error(_error: Error, _message: Message, count: number): ErrorAction { + public error(_error: Error, _message: Message, count: number): ErrorHandlerResult { if (count && count <= 3) { logger.error(`${this.name} server encountered error: ${_message}, ${_error && _error.toString()}`); - return ErrorAction.Continue; + return { + action: ErrorAction.Continue + }; } logger.error(`${this.name} server encountered error and will shut down: ${_message}, ${_error && _error.toString()}`); - return ErrorAction.Shutdown; + return { + action: ErrorAction.Shutdown + }; } - public closed(): CloseAction { + public closed(): CloseHandlerResult { this.restarts.push(Date.now()); if (this.restarts.length < 5) { logger.error(`The ${this.name} server crashed and will restart.`); - return CloseAction.Restart; + return { + action: CloseAction.Restart + }; } else { const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; if (diff <= 3 * 60 * 1000) { @@ -36,12 +42,16 @@ export class ClientErrorHandler implements ErrorHandler { commands.executeCommand(Commands.OPEN_LOGS); } }); - return CloseAction.DoNotRestart; + return { + action: CloseAction.DoNotRestart + }; } logger.error(`The ${this.name} server crashed and will restart.`); this.restarts.shift(); - return CloseAction.Restart; + return { + action: CloseAction.Restart + }; } } } diff --git a/src/extension.ts b/src/extension.ts index 1dfeac1f06..3a61a0321d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,7 +6,7 @@ import * as fse from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; import { CodeActionContext, CodeActionTriggerKind, commands, ConfigurationTarget, Diagnostic, env, EventEmitter, ExtensionContext, extensions, IndentAction, InputBoxOptions, languages, RelativePattern, TextDocument, UIKind, Uri, ViewColumn, window, workspace, WorkspaceConfiguration } from 'vscode'; -import { CancellationToken, CodeActionParams, CodeActionRequest, Command, DidChangeConfigurationNotification, ExecuteCommandParams, ExecuteCommandRequest, LanguageClientOptions, RevealOutputChannelOn } from 'vscode-languageclient'; +import { CancellationToken, CodeActionParams, CodeActionRequest, Command, DidChangeConfigurationNotification, ExecuteCommandParams, ExecuteCommandRequest, LanguageClientOptions, RevealOutputChannelOn, State } from 'vscode-languageclient'; import { LanguageClient } from 'vscode-languageclient/node'; import { apiManager } from './apiManager'; import { ClientErrorHandler } from './clientErrorHandler'; @@ -167,7 +167,6 @@ export function activate(context: ExtensionContext): Promise { resolveAdditionalTextEditsSupport: true, advancedIntroduceParameterRefactoringSupport: true, actionableRuntimeNotificationSupport: true, - shouldLanguageServerExitOnShutdown: true, onCompletionItemSelectedCommand: "editor.action.triggerParameterHints", extractInterfaceSupport: true, }, @@ -175,8 +174,8 @@ export function activate(context: ExtensionContext): Promise { }, middleware: { workspace: { - didChangeConfiguration: () => { - standardClient.getClient().sendNotification(DidChangeConfigurationNotification.type, { + didChangeConfiguration: async () => { + await standardClient.getClient().sendNotification(DidChangeConfigurationNotification.type, { settings: { java: getJavaConfig(requirements.java_home), } @@ -185,12 +184,12 @@ export function activate(context: ExtensionContext): Promise { }, // https://github.com/redhat-developer/vscode-java/issues/2130 // include all diagnostics for the current line in the CodeActionContext params for the performance reason - provideCodeActions: (document, range, context, token, next) => { + provideCodeActions: async (document, range, context, token, next) => { const client: LanguageClient = standardClient.getClient(); const params: CodeActionParams = { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), range: client.code2ProtocolConverter.asRange(range), - context: client.code2ProtocolConverter.asCodeActionContext(context) + context: await client.code2ProtocolConverter.asCodeActionContext(context) }; const showAt = getJavaConfiguration().get("quickfix.showAt"); if (showAt === 'line' && range.start.line === range.end.line && range.start.character === range.end.character) { @@ -209,12 +208,12 @@ export function activate(context: ExtensionContext): Promise { const codeActionContext: CodeActionContext = { diagnostics: allDiagnostics, only: context.only, - triggerKind: CodeActionTriggerKind.Invoke, + triggerKind: context.triggerKind, }; - params.context = client.code2ProtocolConverter.asCodeActionContext(codeActionContext); + params.context = await client.code2ProtocolConverter.asCodeActionContext(codeActionContext); } } - return client.sendRequest(CodeActionRequest.type, params, token).then((values) => { + return client.sendRequest(CodeActionRequest.type, params, token).then(async (values) => { if (values === null) { return undefined; } @@ -224,7 +223,7 @@ export function activate(context: ExtensionContext): Promise { result.push(client.protocol2CodeConverter.asCommand(item)); } else { - result.push(client.protocol2CodeConverter.asCodeAction(item)); + result.push(await client.protocol2CodeConverter.asCodeAction(item)); } } return result; @@ -249,13 +248,16 @@ export function activate(context: ExtensionContext): Promise { // no need to pass `resolve` into any code past this point, // since `resolve` is a no-op from now on + const serverOptions = prepareExecutable(requirements, syntaxServerWorkspacePath, getJavaConfig(requirements.java_home), context, true); if (requireSyntaxServer) { if (process.env['SYNTAXLS_CLIENT_PORT']) { syntaxClient.initialize(requirements, clientOptions); } else { - syntaxClient.initialize(requirements, clientOptions, prepareExecutable(requirements, syntaxServerWorkspacePath, getJavaConfig(requirements.java_home), context, true)); + syntaxClient.initialize(requirements, clientOptions, serverOptions); } - syntaxClient.start(); + syntaxClient.start().then(() => { + syntaxClient.registerSyntaxClientActions(serverOptions); + }); serverStatusBarProvider.showLightWeightStatus(); } @@ -430,7 +432,9 @@ async function startStandardServer(context: ExtensionContext, requirements: requ apiManager.fireDidServerModeChange(ServerMode.hybrid); } await standardClient.initialize(context, requirements, clientOptions, workspacePath, jdtEventEmitter); - standardClient.start(); + standardClient.start().then(async () => { + standardClient.registerLanguageClientActions(context, await fse.pathExists(path.join(workspacePath, ".metadata", ".plugins")), jdtEventEmitter); + }); serverStatusBarProvider.showStandardStatus(); } @@ -532,7 +536,9 @@ export async function getActiveLanguageClient(): Promise { - const codeWorkspaceEdit = languageClient.protocol2CodeConverter.asWorkspaceEdit(workspaceEdit); + const codeWorkspaceEdit = await languageClient.protocol2CodeConverter.asWorkspaceEdit(workspaceEdit); if (!codeWorkspaceEdit) { return; } diff --git a/src/standardLanguageClient.ts b/src/standardLanguageClient.ts index 70828f0084..9819ebd936 100644 --- a/src/standardLanguageClient.ts +++ b/src/standardLanguageClient.ts @@ -5,7 +5,7 @@ import { findRuntimes } from "jdk-utils"; import * as net from 'net'; import * as path from 'path'; import { CancellationToken, CodeActionKind, commands, ConfigurationTarget, DocumentSelector, EventEmitter, ExtensionContext, extensions, languages, Location, ProgressLocation, TextEditor, Uri, ViewColumn, window, workspace } from "vscode"; -import { ConfigurationParams, ConfigurationRequest, LanguageClientOptions, Location as LSLocation, MessageType, Position as LSPosition, TextDocumentPositionParams } from "vscode-languageclient"; +import { ConfigurationParams, ConfigurationRequest, LanguageClientOptions, Location as LSLocation, MessageType, Position as LSPosition, TextDocumentPositionParams, WorkspaceEdit } from "vscode-languageclient"; import { LanguageClient, StreamInfo } from "vscode-languageclient/node"; import { apiManager } from "./apiManager"; import * as buildPath from './buildpath'; @@ -61,8 +61,6 @@ export class StandardLanguageClient { return; } - const hasImported: boolean = await fse.pathExists(path.join(workspacePath, ".metadata", ".plugins")); - if (workspace.getConfiguration().get("java.showBuildStatusOnStart.enabled") === "terminal") { commands.executeCommand(Commands.SHOW_SERVER_TASK_STATUS); } @@ -108,505 +106,506 @@ export class StandardLanguageClient { // Create the language client and start the client. this.languageClient = new LanguageClient('java', extensionName, serverOptions, clientOptions); - this.languageClient.onReady().then(() => { - activationProgressNotification.showProgress(); - this.languageClient.onNotification(StatusNotification.type, (report) => { - switch (report.type) { - case 'ServiceReady': - apiManager.updateServerMode(ServerMode.standard); - apiManager.fireDidServerModeChange(ServerMode.standard); - apiManager.resolveServerReadyPromise(); - - if (extensions.onDidChange) {// Theia doesn't support this API yet - extensions.onDidChange(async () => { - await onExtensionChange(extensions.all); - }); - } + this.registerCommandsForStandardServer(context, jdtEventEmitter); + fileEventHandler.registerFileEventHandlers(this.languageClient, context); - registerPasteEventHandler(context, this.languageClient); - activationProgressNotification.hide(); - if (!hasImported) { - showImportFinishNotification(context); - } - checkLombokDependency(context); - apiManager.getApiInstance().onDidClasspathUpdate((e: Uri) => { - checkLombokDependency(context); + collectBuildFilePattern(extensions.all); + + this.status = ClientStatus.initialized; + } + + public registerLanguageClientActions(context: ExtensionContext, hasImported: boolean, jdtEventEmitter: EventEmitter) { + activationProgressNotification.showProgress(); + this.languageClient.onNotification(StatusNotification.type, (report) => { + switch (report.type) { + case 'ServiceReady': + apiManager.updateServerMode(ServerMode.standard); + apiManager.fireDidServerModeChange(ServerMode.standard); + apiManager.resolveServerReadyPromise(); + + if (extensions.onDidChange) {// Theia doesn't support this API yet + extensions.onDidChange(async () => { + await onExtensionChange(extensions.all); }); - // Disable the client-side snippet provider since LS is ready. - snippetCompletionProvider.dispose(); - break; - case 'Started': + } + + registerPasteEventHandler(context, this.languageClient); + activationProgressNotification.hide(); + if (!hasImported) { + showImportFinishNotification(context); + } + checkLombokDependency(context); + apiManager.getApiInstance().onDidClasspathUpdate((e: Uri) => { + checkLombokDependency(context); + }); + // Disable the client-side snippet provider since LS is ready. + snippetCompletionProvider.dispose(); + break; + case 'Started': + this.status = ClientStatus.started; + serverStatus.updateServerStatus(ServerStatusKind.ready); + commands.executeCommand('setContext', 'javaLSReady', true); + apiManager.updateStatus(ClientStatus.started); + break; + case 'Error': + this.status = ClientStatus.error; + serverStatus.updateServerStatus(ServerStatusKind.error); + apiManager.updateStatus(ClientStatus.error); + break; + case 'ProjectStatus': + if (report.message === "WARNING") { + serverStatus.updateServerStatus(ServerStatusKind.warning); + } else if (report.message === "OK") { this.status = ClientStatus.started; + serverStatus.errorResolved(); serverStatus.updateServerStatus(ServerStatusKind.ready); - commands.executeCommand('setContext', 'javaLSReady', true); - apiManager.updateStatus(ClientStatus.started); - break; - case 'Error': - this.status = ClientStatus.error; - serverStatus.updateServerStatus(ServerStatusKind.error); - apiManager.updateStatus(ClientStatus.error); - break; - case 'ProjectStatus': - if (report.message === "WARNING") { - serverStatus.updateServerStatus(ServerStatusKind.warning); - } else if (report.message === "OK") { - this.status = ClientStatus.started; - serverStatus.errorResolved(); - serverStatus.updateServerStatus(ServerStatusKind.ready); - } - return; - case 'Starting': - case 'Message': - // message goes to progress report instead - break; - } - if (!serverStatus.hasErrors()) { - serverStatusBarProvider.updateTooltip(report.message); - } - }); + } + return; + case 'Starting': + case 'Message': + // message goes to progress report instead + break; + } + if (!serverStatus.hasErrors()) { + serverStatusBarProvider.updateTooltip(report.message); + } + }); - this.languageClient.onNotification(ProgressReportNotification.type, (progress) => { - serverTasks.updateServerTask(progress); - }); + this.languageClient.onNotification(ProgressReportNotification.type, (progress) => { + serverTasks.updateServerTask(progress); + }); - this.languageClient.onNotification(EventNotification.type, async (notification) => { - switch (notification.eventType) { - case EventType.classpathUpdated: - apiManager.fireDidClasspathUpdate(Uri.parse(notification.data)); - break; - case EventType.projectsImported: - const projectUris: Uri[] = []; - if (notification.data) { - for (const uriString of notification.data) { - projectUris.push(Uri.parse(uriString)); - } - } - if (projectUris.length > 0) { - apiManager.fireDidProjectsImport(projectUris); + this.languageClient.onNotification(EventNotification.type, async (notification) => { + switch (notification.eventType) { + case EventType.classpathUpdated: + apiManager.fireDidClasspathUpdate(Uri.parse(notification.data)); + break; + case EventType.projectsImported: + const projectUris: Uri[] = []; + if (notification.data) { + for (const uriString of notification.data) { + projectUris.push(Uri.parse(uriString)); } - break; - case EventType.incompatibleGradleJdkIssue: - const options: string[] = []; - const info = notification.data as GradleCompatibilityInfo; - const highestJavaVersion = Number(info.highestJavaVersion); - let runtimes = await findRuntimes({checkJavac: true, withVersion: true, withTags: true}); - runtimes = runtimes.filter(runtime => { - return runtime.version.major <= highestJavaVersion; + } + if (projectUris.length > 0) { + apiManager.fireDidProjectsImport(projectUris); + } + break; + case EventType.incompatibleGradleJdkIssue: + const options: string[] = []; + const info = notification.data as GradleCompatibilityInfo; + const highestJavaVersion = Number(info.highestJavaVersion); + let runtimes = await findRuntimes({ checkJavac: true, withVersion: true, withTags: true }); + runtimes = runtimes.filter(runtime => { + return runtime.version.major <= highestJavaVersion; + }); + sortJdksByVersion(runtimes); + sortJdksBySource(runtimes); + options.push(UPGRADE_GRADLE + info.recommendedGradleVersion); + if (!runtimes.length) { + options.push(GET_JDK); + } else { + options.push(USE_JAVA + runtimes[0].version.major + AS_GRADLE_JVM); + } + this.showGradleCompatibilityIssueNotification(info.message, options, info.projectUri, info.recommendedGradleVersion, runtimes[0]?.homedir); + break; + case EventType.upgradeGradleWrapper: + const neverShow: boolean | undefined = context.globalState.get("java.neverShowUpgradeWrapperNotification"); + if (!neverShow) { + const upgradeInfo = notification.data as UpgradeGradleWrapperInfo; + const option = `Upgrade to ${upgradeInfo.recommendedGradleVersion}`; + window.showWarningMessage(upgradeInfo.message, option, "Don't show again").then(async (choice) => { + if (choice === option) { + await upgradeGradle(upgradeInfo.projectUri, upgradeInfo.recommendedGradleVersion); + } else if (choice === "Don't show again") { + context.globalState.update("java.neverShowUpgradeWrapperNotification", true); + } }); - sortJdksByVersion(runtimes); - sortJdksBySource(runtimes); - options.push(UPGRADE_GRADLE + info.recommendedGradleVersion); - if (!runtimes.length) { - options.push(GET_JDK); - } else { - options.push(USE_JAVA + runtimes[0].version.major + AS_GRADLE_JVM); - } - this.showGradleCompatibilityIssueNotification(info.message, options, info.projectUri, info.recommendedGradleVersion, runtimes[0]?.homedir); - break; - case EventType.upgradeGradleWrapper: - const neverShow: boolean | undefined = context.globalState.get("java.neverShowUpgradeWrapperNotification"); - if (!neverShow) { - const upgradeInfo = notification.data as UpgradeGradleWrapperInfo; - const option = `Upgrade to ${upgradeInfo.recommendedGradleVersion}`; - window.showWarningMessage(upgradeInfo.message, option, "Don't show again").then(async (choice) => { - if (choice === option) { - await upgradeGradle(upgradeInfo.projectUri, upgradeInfo.recommendedGradleVersion); - } else if (choice === "Don't show again") { - context.globalState.update("java.neverShowUpgradeWrapperNotification", true); - } - }); - } - break; - default: - break; - } - }); + } + break; + default: + break; + } + }); - this.languageClient.onNotification(ActionableNotification.type, (notification) => { - let show = null; - switch (notification.severity) { - case MessageType.Log: - show = logNotification; - break; - case MessageType.Info: - show = window.showInformationMessage; - break; - case MessageType.Warning: - show = window.showWarningMessage; - break; - case MessageType.Error: - show = window.showErrorMessage; + this.languageClient.onNotification(ActionableNotification.type, (notification) => { + let show = null; + switch (notification.severity) { + case MessageType.Log: + show = logNotification; + break; + case MessageType.Info: + show = window.showInformationMessage; + break; + case MessageType.Warning: + show = window.showWarningMessage; + break; + case MessageType.Error: + show = window.showErrorMessage; + break; + } + if (!show) { + return; + } + const titles = notification.commands.map(a => a.title); + show(notification.message, ...titles).then((selection) => { + for (const action of notification.commands) { + if (action.title === selection) { + const args: any[] = (action.arguments) ? action.arguments : []; + commands.executeCommand(action.command, ...args); break; - } - if (!show) { - return; - } - const titles = notification.commands.map(a => a.title); - show(notification.message, ...titles).then((selection) => { - for (const action of notification.commands) { - if (action.title === selection) { - const args: any[] = (action.arguments) ? action.arguments : []; - commands.executeCommand(action.command, ...args); - break; - } } - }); + } }); + }); - this.languageClient.onRequest(ExecuteClientCommandRequest.type, (params) => { - return commands.executeCommand(params.command, ...params.arguments); - }); + this.languageClient.onRequest(ExecuteClientCommandRequest.type, (params) => { + return commands.executeCommand(params.command, ...params.arguments); + }); - this.languageClient.onNotification(ServerNotification.type, (params) => { - commands.executeCommand(params.command, ...params.arguments); - }); + this.languageClient.onNotification(ServerNotification.type, (params) => { + commands.executeCommand(params.command, ...params.arguments); + }); - this.languageClient.onRequest(ConfigurationRequest.type, (params: ConfigurationParams) => { - const result: any[] = []; - const activeEditor: TextEditor | undefined = window.activeTextEditor; - for (const item of params.items) { - const scopeUri: Uri | undefined = item.scopeUri && Uri.parse(item.scopeUri); - if (scopeUri && scopeUri.toString() === activeEditor?.document.uri.toString()) { - if (item.section === "java.format.insertSpaces") { - result.push(activeEditor.options.insertSpaces); - } else if (item.section === "java.format.tabSize") { - result.push(activeEditor.options.tabSize); - } else { - result.push(null); - } + this.languageClient.onRequest(ConfigurationRequest.type, (params: ConfigurationParams) => { + const result: any[] = []; + const activeEditor: TextEditor | undefined = window.activeTextEditor; + for (const item of params.items) { + const scopeUri: Uri | undefined = item.scopeUri && Uri.parse(item.scopeUri); + if (scopeUri && scopeUri.toString() === activeEditor?.document.uri.toString()) { + if (item.section === "java.format.insertSpaces") { + result.push(activeEditor.options.insertSpaces); + } else if (item.section === "java.format.tabSize") { + result.push(activeEditor.options.tabSize); } else { - result.push(workspace.getConfiguration(null, scopeUri).get(item.section, null /* defaultValue */)); + result.push(null); } + } else { + result.push(workspace.getConfiguration(null, scopeUri).get(item.section, null /* defaultValue */)); } - return result; - }); - }); - - this.registerCommandsForStandardServer(context, jdtEventEmitter); - fileEventHandler.registerFileEventHandlers(this.languageClient, context); - - collectBuildFilePattern(extensions.all); - - this.status = ClientStatus.initialized; - } - - private showGradleCompatibilityIssueNotification(message: string, options: string[], projectUri: string, gradleVersion: string, newJavaHome: string) { - window.showErrorMessage(`${message} [Learn More](https://docs.gradle.org/current/userguide/compatibility.html)`, ...options).then(async (choice) => { - if (choice === GET_JDK) { - commands.executeCommand(Commands.OPEN_BROWSER, Uri.parse(getJdkUrl())); - } else if (choice.startsWith(USE_JAVA)) { - await workspace.getConfiguration().update(GRADLE_IMPORT_JVM, newJavaHome, ConfigurationTarget.Global); - commands.executeCommand("workbench.action.openSettings", GRADLE_IMPORT_JVM); - commands.executeCommand(Commands.IMPORT_PROJECTS_CMD); - } else if (choice.startsWith(UPGRADE_GRADLE)) { - await upgradeGradle(projectUri, gradleVersion); } + return result; }); - } - private registerCommandsForStandardServer(context: ExtensionContext, jdtEventEmitter: EventEmitter): void { - context.subscriptions.push(commands.registerCommand(Commands.IMPORT_PROJECTS_CMD, async () => { - return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.IMPORT_PROJECTS); + context.subscriptions.push(commands.registerCommand(GRADLE_CHECKSUM, (wrapper: string, sha256: string) => { + setGradleWrapperChecksum(wrapper, sha256); })); - context.subscriptions.push(commands.registerCommand(Commands.OPEN_OUTPUT, () => this.languageClient.outputChannel.show(ViewColumn.Three))); - context.subscriptions.push(commands.registerCommand(Commands.SHOW_SERVER_TASK_STATUS, () => serverTaskPresenter.presentServerTaskView())); - - this.languageClient.onReady().then(() => { - context.subscriptions.push(commands.registerCommand(GRADLE_CHECKSUM, (wrapper: string, sha256: string) => { - setGradleWrapperChecksum(wrapper, sha256); - })); - - context.subscriptions.push(commands.registerCommand(Commands.SHOW_JAVA_REFERENCES, (uri: string, position: LSPosition, locations: LSLocation[]) => { - commands.executeCommand(Commands.SHOW_REFERENCES, Uri.parse(uri), this.languageClient.protocol2CodeConverter.asPosition(position), locations.map(this.languageClient.protocol2CodeConverter.asLocation)); - })); - context.subscriptions.push(commands.registerCommand(Commands.SHOW_JAVA_IMPLEMENTATIONS, (uri: string, position: LSPosition, locations: LSLocation[]) => { - commands.executeCommand(Commands.SHOW_REFERENCES, Uri.parse(uri), this.languageClient.protocol2CodeConverter.asPosition(position), locations.map(this.languageClient.protocol2CodeConverter.asLocation)); - })); + context.subscriptions.push(commands.registerCommand(Commands.SHOW_JAVA_REFERENCES, (uri: string, position: LSPosition, locations: LSLocation[]) => { + commands.executeCommand(Commands.SHOW_REFERENCES, Uri.parse(uri), this.languageClient.protocol2CodeConverter.asPosition(position), locations.map(this.languageClient.protocol2CodeConverter.asLocation)); + })); + context.subscriptions.push(commands.registerCommand(Commands.SHOW_JAVA_IMPLEMENTATIONS, (uri: string, position: LSPosition, locations: LSLocation[]) => { + commands.executeCommand(Commands.SHOW_REFERENCES, Uri.parse(uri), this.languageClient.protocol2CodeConverter.asPosition(position), locations.map(this.languageClient.protocol2CodeConverter.asLocation)); + })); - context.subscriptions.push(commands.registerCommand(Commands.CONFIGURATION_UPDATE, uri => projectConfigurationUpdate(this.languageClient, uri))); + context.subscriptions.push(commands.registerCommand(Commands.CONFIGURATION_UPDATE, async (uri) => { + await projectConfigurationUpdate(this.languageClient, uri); + })); - context.subscriptions.push(commands.registerCommand(Commands.IGNORE_INCOMPLETE_CLASSPATH, () => setIncompleteClasspathSeverity('ignore'))); + context.subscriptions.push(commands.registerCommand(Commands.IGNORE_INCOMPLETE_CLASSPATH, () => setIncompleteClasspathSeverity('ignore'))); - context.subscriptions.push(commands.registerCommand(Commands.IGNORE_INCOMPLETE_CLASSPATH_HELP, () => { - commands.executeCommand(Commands.OPEN_BROWSER, Uri.parse('https://github.com/redhat-developer/vscode-java/wiki/%22Classpath-is-incomplete%22-warning')); - })); + context.subscriptions.push(commands.registerCommand(Commands.IGNORE_INCOMPLETE_CLASSPATH_HELP, () => { + commands.executeCommand(Commands.OPEN_BROWSER, Uri.parse('https://github.com/redhat-developer/vscode-java/wiki/%22Classpath-is-incomplete%22-warning')); + })); - context.subscriptions.push(commands.registerCommand(Commands.PROJECT_CONFIGURATION_STATUS, (uri, status) => setProjectConfigurationUpdate(this.languageClient, uri, status))); + context.subscriptions.push(commands.registerCommand(Commands.PROJECT_CONFIGURATION_STATUS, async (uri, status) => { + await setProjectConfigurationUpdate(this.languageClient, uri, status); + })); - context.subscriptions.push(commands.registerCommand(Commands.NULL_ANALYSIS_SET_MODE, (status) => setNullAnalysisStatus(status))); + context.subscriptions.push(commands.registerCommand(Commands.NULL_ANALYSIS_SET_MODE, (status) => setNullAnalysisStatus(status))); - context.subscriptions.push(commands.registerCommand(Commands.APPLY_WORKSPACE_EDIT, (obj) => { - applyWorkspaceEdit(obj, this.languageClient); - })); + context.subscriptions.push(commands.registerCommand(Commands.APPLY_WORKSPACE_EDIT, (obj) => { + applyWorkspaceEdit(obj, this.languageClient); + })); - context.subscriptions.push(commands.registerCommand(Commands.NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND, async (location: LinkLocation | Uri) => { - let superImplLocation: Location | undefined; + context.subscriptions.push(commands.registerCommand(Commands.NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND, async (location: LinkLocation | Uri) => { + let superImplLocation: Location | undefined; - if (!location) { // comes from command palette - if (window.activeTextEditor?.document.languageId !== "java") { - return; - } - location = window.activeTextEditor.document.uri; + if (!location) { // comes from command palette + if (window.activeTextEditor?.document.languageId !== "java") { + return; } + location = window.activeTextEditor.document.uri; + } - if (location instanceof Uri) { // comes from context menu - const params: TextDocumentPositionParams = { - textDocument: { - uri: location.toString(), - }, - position: this.languageClient.code2ProtocolConverter.asPosition(window.activeTextEditor.selection.active), - }; - const response = await this.languageClient.sendRequest(FindLinks.type, { - type: 'superImplementation', - position: params, - }); + if (location instanceof Uri) { // comes from context menu + const params: TextDocumentPositionParams = { + textDocument: { + uri: location.toString(), + }, + position: this.languageClient.code2ProtocolConverter.asPosition(window.activeTextEditor.selection.active), + }; + const response = await this.languageClient.sendRequest(FindLinks.type, { + type: 'superImplementation', + position: params, + }); - if (response && response.length > 0) { - const superImpl = response[0]; - superImplLocation = new Location( - Uri.parse(superImpl.uri), - this.languageClient.protocol2CodeConverter.asRange(superImpl.range) - ); - } - } else { // comes from hover information + if (response && response.length > 0) { + const superImpl = response[0]; superImplLocation = new Location( - Uri.parse(decodeBase64(location.uri)), - this.languageClient.protocol2CodeConverter.asRange(location.range), + Uri.parse(superImpl.uri), + this.languageClient.protocol2CodeConverter.asRange(superImpl.range) ); } + } else { // comes from hover information + superImplLocation = new Location( + Uri.parse(decodeBase64(location.uri)), + this.languageClient.protocol2CodeConverter.asRange(location.range), + ); + } - if (superImplLocation) { - return window.showTextDocument(superImplLocation.uri, { - preserveFocus: true, - selection: superImplLocation.range, - }); - } else { - return showNoLocationFound('No super implementation found'); - } - })); + if (superImplLocation) { + return window.showTextDocument(superImplLocation.uri, { + preserveFocus: true, + selection: superImplLocation.range, + }); + } else { + return showNoLocationFound('No super implementation found'); + } + })); - context.subscriptions.push(commands.registerCommand(Commands.SHOW_TYPE_HIERARCHY, (location: any) => { - if (location instanceof Uri) { - typeHierarchyTree.setTypeHierarchy(new Location(location, window.activeTextEditor.selection.active), TypeHierarchyDirection.both); - } else { - if (window.activeTextEditor?.document?.languageId !== "java") { - return; - } - typeHierarchyTree.setTypeHierarchy(new Location(window.activeTextEditor.document.uri, window.activeTextEditor.selection.active), TypeHierarchyDirection.both); - } - })); - - context.subscriptions.push(commands.registerCommand(Commands.SHOW_CLASS_HIERARCHY, () => { - typeHierarchyTree.changeDirection(TypeHierarchyDirection.both); - })); - - context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUPERTYPE_HIERARCHY, () => { - typeHierarchyTree.changeDirection(TypeHierarchyDirection.parents); - })); - - context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUBTYPE_HIERARCHY, () => { - typeHierarchyTree.changeDirection(TypeHierarchyDirection.children); - })); - - context.subscriptions.push(commands.registerCommand(Commands.CHANGE_BASE_TYPE, async (item: TypeHierarchyItem) => { - typeHierarchyTree.changeBaseItem(item); - })); - - context.subscriptions.push(commands.registerCommand(Commands.BUILD_PROJECT, async (uris: Uri[] | Uri, isFullBuild: boolean, token: CancellationToken) => { - let resources: Uri[] = []; - if (uris instanceof Uri) { - resources.push(uris); - } else if (Array.isArray(uris)) { - for (const uri of uris) { - if (uri instanceof Uri) { - resources.push(uri); - } - } + context.subscriptions.push(commands.registerCommand(Commands.SHOW_TYPE_HIERARCHY, (location: any) => { + if (location instanceof Uri) { + typeHierarchyTree.setTypeHierarchy(new Location(location, window.activeTextEditor.selection.active), TypeHierarchyDirection.both); + } else { + if (window.activeTextEditor?.document?.languageId !== "java") { + return; } + typeHierarchyTree.setTypeHierarchy(new Location(window.activeTextEditor.document.uri, window.activeTextEditor.selection.active), TypeHierarchyDirection.both); + } + })); - if (!resources.length) { - resources = await askForProjects( - window.activeTextEditor?.document.uri, - "Please select the project(s) to rebuild.", - ); - if (!resources?.length) { - return; - } - } + context.subscriptions.push(commands.registerCommand(Commands.SHOW_CLASS_HIERARCHY, () => { + typeHierarchyTree.changeDirection(TypeHierarchyDirection.both); + })); - const params: BuildProjectParams = { - identifiers: resources.map((u => { - return { uri: u.toString() }; - })), - // we can consider expose 'isFullBuild' according to users' feedback, - // currently set it to true by default. - isFullBuild: isFullBuild === undefined ? true : isFullBuild, - }; + context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUPERTYPE_HIERARCHY, () => { + typeHierarchyTree.changeDirection(TypeHierarchyDirection.parents); + })); - return window.withProgress({ location: ProgressLocation.Window }, async p => { - p.report({ message: 'Rebuilding projects...' }); - return new Promise(async (resolve, reject) => { - const start = new Date().getTime(); - - let res: CompileWorkspaceStatus; - try { - res = token ? await this.languageClient.sendRequest(BuildProjectRequest.type, params, token) : - await this.languageClient.sendRequest(BuildProjectRequest.type, params); - } catch (error) { - if (error && error.code === -32800) { // Check if the request is cancelled. - res = CompileWorkspaceStatus.cancelled; - } - reject(error); - } + context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUBTYPE_HIERARCHY, () => { + typeHierarchyTree.changeDirection(TypeHierarchyDirection.children); + })); - const elapsed = new Date().getTime() - start; - const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; - setTimeout(() => { // set a timeout so user would still see the message when build time is short - resolve(res); - }, humanVisibleDelay); - }); - }); - })); + context.subscriptions.push(commands.registerCommand(Commands.CHANGE_BASE_TYPE, async (item: TypeHierarchyItem) => { + typeHierarchyTree.changeBaseItem(item); + })); - context.subscriptions.push(commands.registerCommand(Commands.COMPILE_WORKSPACE, (isFullCompile: boolean, token?: CancellationToken) => { - return window.withProgress({ location: ProgressLocation.Window }, async p => { - if (typeof isFullCompile !== 'boolean') { - const selection = await window.showQuickPick(['Incremental', 'Full'], { placeHolder: 'please choose compile type:' }); - isFullCompile = selection !== 'Incremental'; + context.subscriptions.push(commands.registerCommand(Commands.BUILD_PROJECT, async (uris: Uri[] | Uri, isFullBuild: boolean, token: CancellationToken) => { + let resources: Uri[] = []; + if (uris instanceof Uri) { + resources.push(uris); + } else if (Array.isArray(uris)) { + for (const uri of uris) { + if (uri instanceof Uri) { + resources.push(uri); } - p.report({ message: 'Compiling workspace...' }); + } + } + + if (!resources.length) { + resources = await askForProjects( + window.activeTextEditor?.document.uri, + "Please select the project(s) to rebuild.", + ); + if (!resources?.length) { + return; + } + } + + const params: BuildProjectParams = { + identifiers: resources.map((u => { + return { uri: u.toString() }; + })), + // we can consider expose 'isFullBuild' according to users' feedback, + // currently set it to true by default. + isFullBuild: isFullBuild === undefined ? true : isFullBuild, + }; + + return window.withProgress({ location: ProgressLocation.Window }, async p => { + p.report({ message: 'Rebuilding projects...' }); + return new Promise(async (resolve, reject) => { const start = new Date().getTime(); + let res: CompileWorkspaceStatus; try { - res = token ? await this.languageClient.sendRequest(CompileWorkspaceRequest.type, isFullCompile, token) - : await this.languageClient.sendRequest(CompileWorkspaceRequest.type, isFullCompile); + res = token ? await this.languageClient.sendRequest(BuildProjectRequest.type, params, token) : + await this.languageClient.sendRequest(BuildProjectRequest.type, params); } catch (error) { if (error && error.code === -32800) { // Check if the request is cancelled. res = CompileWorkspaceStatus.cancelled; - } else { - throw error; } + reject(error); } const elapsed = new Date().getTime() - start; const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; - return new Promise((resolve, reject) => { - setTimeout(() => { // set a timeout so user would still see the message when build time is short - if (res === CompileWorkspaceStatus.succeed) { - resolve(res); - } else { - reject(res); - } - }, humanVisibleDelay); - }); + setTimeout(() => { // set a timeout so user would still see the message when build time is short + resolve(res); + }, humanVisibleDelay); }); - })); + }); + })); - context.subscriptions.push(commands.registerCommand(Commands.UPDATE_SOURCE_ATTACHMENT_CMD, async (classFileUri: Uri): Promise => { - const resolveRequest: SourceAttachmentRequest = { - classFileUri: classFileUri.toString(), - }; - const resolveResult: SourceAttachmentResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.RESOLVE_SOURCE_ATTACHMENT, JSON.stringify(resolveRequest)); - if (resolveResult.errorMessage) { - window.showErrorMessage(resolveResult.errorMessage); - return false; + context.subscriptions.push(commands.registerCommand(Commands.COMPILE_WORKSPACE, (isFullCompile: boolean, token?: CancellationToken) => { + return window.withProgress({ location: ProgressLocation.Window }, async p => { + if (typeof isFullCompile !== 'boolean') { + const selection = await window.showQuickPick(['Incremental', 'Full'], { placeHolder: 'please choose compile type:' }); + isFullCompile = selection !== 'Incremental'; + } + p.report({ message: 'Compiling workspace...' }); + const start = new Date().getTime(); + let res: CompileWorkspaceStatus; + try { + res = token ? await this.languageClient.sendRequest(CompileWorkspaceRequest.type, isFullCompile, token) + : await this.languageClient.sendRequest(CompileWorkspaceRequest.type, isFullCompile); + } catch (error) { + if (error && error.code === -32800) { // Check if the request is cancelled. + res = CompileWorkspaceStatus.cancelled; + } else { + throw error; + } } - const attributes: SourceAttachmentAttribute = resolveResult.attributes || {}; - const defaultPath = attributes.sourceAttachmentPath || attributes.jarPath; - const sourceFileUris: Uri[] = await window.showOpenDialog({ - defaultUri: defaultPath ? Uri.file(defaultPath) : null, - openLabel: 'Select Source File', - canSelectFiles: true, - canSelectFolders: false, - canSelectMany: false, - filters: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'Source files': ['jar', 'zip'] - }, + const elapsed = new Date().getTime() - start; + const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; + return new Promise((resolve, reject) => { + setTimeout(() => { // set a timeout so user would still see the message when build time is short + if (res === CompileWorkspaceStatus.succeed) { + resolve(res); + } else { + reject(res); + } + }, humanVisibleDelay); }); + }); + })); - if (sourceFileUris && sourceFileUris.length) { - const updateRequest: SourceAttachmentRequest = { - classFileUri: classFileUri.toString(), - attributes: { - ...attributes, - sourceAttachmentPath: sourceFileUris[0].fsPath - }, - }; - const updateResult: SourceAttachmentResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.UPDATE_SOURCE_ATTACHMENT, JSON.stringify(updateRequest)); - if (updateResult.errorMessage) { - window.showErrorMessage(updateResult.errorMessage); - return false; - } + context.subscriptions.push(commands.registerCommand(Commands.UPDATE_SOURCE_ATTACHMENT_CMD, async (classFileUri: Uri): Promise => { + const resolveRequest: SourceAttachmentRequest = { + classFileUri: classFileUri.toString(), + }; + const resolveResult: SourceAttachmentResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.RESOLVE_SOURCE_ATTACHMENT, JSON.stringify(resolveRequest)); + if (resolveResult.errorMessage) { + window.showErrorMessage(resolveResult.errorMessage); + return false; + } - // Notify jdt content provider to rerender the classfile contents. - jdtEventEmitter.fire(classFileUri); - return true; + const attributes: SourceAttachmentAttribute = resolveResult.attributes || {}; + const defaultPath = attributes.sourceAttachmentPath || attributes.jarPath; + const sourceFileUris: Uri[] = await window.showOpenDialog({ + defaultUri: defaultPath ? Uri.file(defaultPath) : null, + openLabel: 'Select Source File', + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + filters: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Source files': ['jar', 'zip'] + }, + }); + + if (sourceFileUris && sourceFileUris.length) { + const updateRequest: SourceAttachmentRequest = { + classFileUri: classFileUri.toString(), + attributes: { + ...attributes, + sourceAttachmentPath: sourceFileUris[0].fsPath + }, + }; + const updateResult: SourceAttachmentResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.UPDATE_SOURCE_ATTACHMENT, JSON.stringify(updateRequest)); + if (updateResult.errorMessage) { + window.showErrorMessage(updateResult.errorMessage); + return false; } - })); - buildPath.registerCommands(context); - sourceAction.registerCommands(this.languageClient, context); - refactorAction.registerCommands(this.languageClient, context); - pasteAction.registerCommands(this.languageClient, context); + // Notify jdt content provider to rerender the classfile contents. + jdtEventEmitter.fire(classFileUri); + return true; + } + })); - excludeProjectSettingsFiles(); + buildPath.registerCommands(context); + sourceAction.registerCommands(this.languageClient, context); + refactorAction.registerCommands(this.languageClient, context); + pasteAction.registerCommands(this.languageClient, context); - context.subscriptions.push(languages.registerCodeActionsProvider({ scheme: 'file', language: 'java' }, new RefactorDocumentProvider(), RefactorDocumentProvider.metadata)); - context.subscriptions.push(commands.registerCommand(Commands.LEARN_MORE_ABOUT_REFACTORING, async (kind: CodeActionKind) => { - const sectionId: string = javaRefactorKinds.get(kind) || ''; - markdownPreviewProvider.show(context.asAbsolutePath(path.join('document', `${Commands.LEARN_MORE_ABOUT_REFACTORING}.md`)), 'Java Refactoring', sectionId, context); - })); + excludeProjectSettingsFiles(); - context.subscriptions.push(commands.registerCommand(Commands.CREATE_MODULE_INFO_COMMAND, async () => { - const uri = await askForProjects( - window.activeTextEditor?.document.uri, - "Please select the project to create module-info.java", - false, - ); - if (!uri?.length) { - return; - } + context.subscriptions.push(languages.registerCodeActionsProvider({ scheme: 'file', language: 'java' }, new RefactorDocumentProvider(), RefactorDocumentProvider.metadata)); + context.subscriptions.push(commands.registerCommand(Commands.LEARN_MORE_ABOUT_REFACTORING, async (kind: CodeActionKind) => { + const sectionId: string = javaRefactorKinds.get(kind) || ''; + markdownPreviewProvider.show(context.asAbsolutePath(path.join('document', `${Commands.LEARN_MORE_ABOUT_REFACTORING}.md`)), 'Java Refactoring', sectionId, context); + })); - const moduleInfoUri: string = await commands.executeCommand( - Commands.EXECUTE_WORKSPACE_COMMAND, - Commands.CREATE_MODULE_INFO, - uri[0].toString(), - ); + context.subscriptions.push(commands.registerCommand(Commands.CREATE_MODULE_INFO_COMMAND, async () => { + const uri = await askForProjects( + window.activeTextEditor?.document.uri, + "Please select the project to create module-info.java", + false, + ); + if (!uri?.length) { + return; + } - if (moduleInfoUri) { - await window.showTextDocument(Uri.parse(moduleInfoUri)); - } - })); + const moduleInfoUri: string = await commands.executeCommand( + Commands.EXECUTE_WORKSPACE_COMMAND, + Commands.CREATE_MODULE_INFO, + uri[0].toString(), + ); - context.subscriptions.push(commands.registerCommand(Commands.UPGRADE_GRADLE_WRAPPER, (projectUri: string, version?: string) => { - upgradeGradle(projectUri, version); - })); + if (moduleInfoUri) { + await window.showTextDocument(Uri.parse(moduleInfoUri)); + } + })); - languages.registerCodeActionsProvider({ - language: "xml", - scheme: "file", - pattern: "**/pom.xml" - }, new PomCodeActionProvider(context), pomCodeActionMetadata); + context.subscriptions.push(commands.registerCommand(Commands.UPGRADE_GRADLE_WRAPPER, (projectUri: string, version?: string) => { + upgradeGradle(projectUri, version); + })); - languages.registerCodeActionsProvider({ - scheme: "file", - pattern: "**/{gradle/wrapper/gradle-wrapper.properties,build.gradle,build.gradle.kts,settings.gradle,settings.gradle.kts}" - }, new GradleCodeActionProvider(), gradleCodeActionMetadata); + languages.registerCodeActionsProvider({ + language: "xml", + scheme: "file", + pattern: "**/pom.xml" + }, new PomCodeActionProvider(context), pomCodeActionMetadata); - if (languages.registerInlayHintsProvider) { - context.subscriptions.push(languages.registerInlayHintsProvider(JAVA_SELECTOR, new JavaInlayHintsProvider(this.languageClient))); - } + languages.registerCodeActionsProvider({ + scheme: "file", + pattern: "**/{gradle/wrapper/gradle-wrapper.properties,build.gradle,build.gradle.kts,settings.gradle,settings.gradle.kts}" + }, new GradleCodeActionProvider(), gradleCodeActionMetadata); + + if (languages.registerInlayHintsProvider) { + context.subscriptions.push(languages.registerInlayHintsProvider(JAVA_SELECTOR, new JavaInlayHintsProvider(this.languageClient))); + } + } + private showGradleCompatibilityIssueNotification(message: string, options: string[], projectUri: string, gradleVersion: string, newJavaHome: string) { + window.showErrorMessage(`${message} [Learn More](https://docs.gradle.org/current/userguide/compatibility.html)`, ...options).then(async (choice) => { + if (choice === GET_JDK) { + commands.executeCommand(Commands.OPEN_BROWSER, Uri.parse(getJdkUrl())); + } else if (choice.startsWith(USE_JAVA)) { + await workspace.getConfiguration().update(GRADLE_IMPORT_JVM, newJavaHome, ConfigurationTarget.Global); + commands.executeCommand("workbench.action.openSettings", GRADLE_IMPORT_JVM); + commands.executeCommand(Commands.IMPORT_PROJECTS_CMD); + } else if (choice.startsWith(UPGRADE_GRADLE)) { + await upgradeGradle(projectUri, gradleVersion); + } }); } - public start(): void { + private registerCommandsForStandardServer(context: ExtensionContext, jdtEventEmitter: EventEmitter): void { + context.subscriptions.push(commands.registerCommand(Commands.IMPORT_PROJECTS_CMD, async () => { + return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.IMPORT_PROJECTS); + })); + + context.subscriptions.push(commands.registerCommand(Commands.OPEN_OUTPUT, () => this.languageClient.outputChannel.show(ViewColumn.Three))); + context.subscriptions.push(commands.registerCommand(Commands.SHOW_SERVER_TASK_STATUS, () => serverTaskPresenter.presentServerTaskView())); + } + + public start(): Promise { if (this.languageClient && this.status === ClientStatus.initialized) { - this.languageClient.start(); this.status = ClientStatus.starting; + return this.languageClient.start(); } } @@ -677,7 +676,7 @@ function setIncompleteClasspathSeverity(severity: string) { ); } -function setProjectConfigurationUpdate(languageClient: LanguageClient, uri: Uri, status: FeatureStatus) { +async function setProjectConfigurationUpdate(languageClient: LanguageClient, uri: Uri, status: FeatureStatus) { const config = getJavaConfiguration(); const section = 'configuration.updateBuildConfiguration'; @@ -687,7 +686,7 @@ function setProjectConfigurationUpdate(languageClient: LanguageClient, uri: Uri, (error) => logger.error(error) ); if (status !== FeatureStatus.disabled) { - projectConfigurationUpdate(languageClient, uri); + await projectConfigurationUpdate(languageClient, uri); } } @@ -717,10 +716,10 @@ export function showNoLocationFound(message: string): void { ); } -export function applyWorkspaceEdit(obj, languageClient): Thenable { - const edit = languageClient.protocol2CodeConverter.asWorkspaceEdit(obj); - if (edit) { - return workspace.applyEdit(edit); +export async function applyWorkspaceEdit(workspaceEdit: WorkspaceEdit, languageClient: LanguageClient): Promise { + const codeEdit = await languageClient.protocol2CodeConverter.asWorkspaceEdit(workspaceEdit); + if (codeEdit) { + return await workspace.applyEdit(codeEdit); } else { return Promise.resolve(true); } diff --git a/src/standardLanguageClientUtils.ts b/src/standardLanguageClientUtils.ts index cfe4090787..6b8723e699 100644 --- a/src/standardLanguageClientUtils.ts +++ b/src/standardLanguageClientUtils.ts @@ -33,11 +33,11 @@ export async function projectConfigurationUpdate(languageClient: LanguageClient, } if (resources.length === 1) { - languageClient.sendNotification(ProjectConfigurationUpdateRequest.type, { + await languageClient.sendNotification(ProjectConfigurationUpdateRequest.type, { uri: resources[0].toString(), }); } else if (resources.length > 1) { - languageClient.sendNotification(ProjectConfigurationUpdateRequest.typeV2, { + await languageClient.sendNotification(ProjectConfigurationUpdateRequest.typeV2, { identifiers: resources.map(r => { return { uri: r.toString() }; }), diff --git a/src/syntaxLanguageClient.ts b/src/syntaxLanguageClient.ts index 86ab554508..6af9d42713 100644 --- a/src/syntaxLanguageClient.ts +++ b/src/syntaxLanguageClient.ts @@ -24,8 +24,8 @@ export class SyntaxLanguageClient { const newClientOptions: LanguageClientOptions = Object.assign({}, clientOptions, { middleware: { workspace: { - didChangeConfiguration: () => { - this.languageClient.sendNotification(DidChangeConfigurationNotification.type, { + didChangeConfiguration: async () => { + await this.languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: { java: getJavaConfig(requirements.java_home), } @@ -56,39 +56,41 @@ export class SyntaxLanguageClient { if (serverOptions) { this.languageClient = new LanguageClient('java', extensionName, serverOptions, newClientOptions); - - // TODO: Currently only resolve the promise when the server mode is explicitly set to lightweight. - // This is to avoid breakings - this.languageClient.onReady().then(() => { - this.languageClient.onNotification(StatusNotification.type, (report) => { - switch (report.type) { - case 'Started': - this.status = ClientStatus.started; - apiManager.updateStatus(ClientStatus.started); - // Disable the client-side snippet provider since LS is ready. - snippetCompletionProvider.dispose(); - break; - case 'Error': - this.status = ClientStatus.error; - apiManager.updateStatus(ClientStatus.error); - break; - default: - break; - } - if (apiManager.getApiInstance().serverMode === ServerMode.lightWeight) { - apiManager.fireDidServerModeChange(ServerMode.lightWeight); - } - }); - }); } this.status = ClientStatus.initialized; } - public start(): void { + public registerSyntaxClientActions(serverOptions?: ServerOptions): void { + // TODO: Currently only resolve the promise when the server mode is explicitly set to lightweight. + // This is to avoid breakings + if (serverOptions) { + this.languageClient.onNotification(StatusNotification.type, (report) => { + switch (report.type) { + case 'Started': + this.status = ClientStatus.started; + apiManager.updateStatus(ClientStatus.started); + // Disable the client-side snippet provider since LS is ready. + snippetCompletionProvider.dispose(); + break; + case 'Error': + this.status = ClientStatus.error; + apiManager.updateStatus(ClientStatus.error); + break; + default: + break; + } + if (apiManager.getApiInstance().serverMode === ServerMode.lightWeight) { + apiManager.fireDidServerModeChange(ServerMode.lightWeight); + } + }); + } + } + + public start(): Promise { if (this.languageClient) { - this.languageClient.start(); this.status = ClientStatus.starting; + return this.languageClient.start(); } }