11'use strict';
22
3- import { ExtensionContext, window, workspace, commands, Uri, ProgressLocation, ViewColumn, EventEmitter, extensions, Location, languages, CodeActionKind, TextEditor, CancellationToken } from "vscode";
3+ import { ExtensionContext, window, workspace, commands, Uri, ProgressLocation, ViewColumn, EventEmitter, extensions, Location, languages, CodeActionKind, TextEditor, CancellationToken, ConfigurationTarget, Range, Position } from "vscode";
44import { Commands } from "./commands";
55import { serverStatus, ServerStatusKind } from "./serverStatus";
66import { prepareExecutable, awaitServerConnection } from "./javaServerStarter";
77import { getJavaConfig, applyWorkspaceEdit } from "./extension";
88import { LanguageClientOptions, Position as LSPosition, Location as LSLocation, MessageType, TextDocumentPositionParams, ConfigurationRequest, ConfigurationParams } from "vscode-languageclient";
99import { LanguageClient, StreamInfo } from "vscode-languageclient/node";
10- import { CompileWorkspaceRequest, CompileWorkspaceStatus, SourceAttachmentRequest, SourceAttachmentResult, SourceAttachmentAttribute, ProjectConfigurationUpdateRequest, FeatureStatus, StatusNotification, ProgressReportNotification, ActionableNotification, ExecuteClientCommandRequest, ServerNotification, EventNotification, EventType, LinkLocation, FindLinks } from "./protocol";
10+ import { CompileWorkspaceRequest, CompileWorkspaceStatus, SourceAttachmentRequest, SourceAttachmentResult, SourceAttachmentAttribute, ProjectConfigurationUpdateRequest, FeatureStatus, StatusNotification, ProgressReportNotification, ActionableNotification, ExecuteClientCommandRequest, ServerNotification, EventNotification, EventType, LinkLocation, FindLinks, GradleCompatibilityInfo } from "./protocol";
1111import { setGradleWrapperChecksum, excludeProjectSettingsFiles, ServerMode } from "./settings";
1212import { onExtensionChange, collectBuildFilePattern } from "./plugin";
1313import { activationProgressNotification, serverTaskPresenter } from "./serverTaskPresenter";
14- import { RequirementsData } from "./requirements";
14+ import { getJdkUrl, RequirementsData, sortJdksBySource, sortJdksByVersion } from "./requirements";
1515import * as net from 'net';
1616import * as fse from 'fs-extra';
1717import * as path from 'path';
@@ -32,9 +32,15 @@ import { typeHierarchyTree } from "./typeHierarchy/typeHierarchyTree";
3232import { TypeHierarchyDirection, TypeHierarchyItem } from "./typeHierarchy/protocol";
3333import { buildFilePatterns } from './plugin';
3434import { pomCodeActionMetadata, PomCodeActionProvider } from "./pom/pomCodeActionProvider";
35+ import { findRuntimes, IJavaRuntime } from "jdk-utils";
3536
3637const extensionName = 'Language Support for Java';
3738const GRADLE_CHECKSUM = "gradle/checksum/prompt";
39+ const GET_JDK = "Get the Java Development Kit";
40+ const USE_JAVA = "Use Java ";
41+ const AS_GRADLE_JVM = " as Gradle JVM";
42+ const UPGRADE_GRADLE = "Upgrade Gradle to ";
43+ const GRADLE_IMPORT_JVM = "java.import.gradle.java.home";
3844
3945export class StandardLanguageClient {
4046
@@ -141,7 +147,7 @@ export class StandardLanguageClient {
141147 serverTasks.updateServerTask(progress);
142148 });
143149
144- this.languageClient.onNotification(EventNotification.type, (notification) => {
150+ this.languageClient.onNotification(EventNotification.type, async (notification) => {
145151 switch (notification.eventType) {
146152 case EventType.ClasspathUpdated:
147153 apiManager.fireDidClasspathUpdate(Uri.parse(notification.data));
@@ -157,6 +163,23 @@ export class StandardLanguageClient {
157163 apiManager.fireDidProjectsImport(projectUris);
158164 }
159165 break;
166+ case EventType.IncompatibleGradleJdkIssue:
167+ const options: string[] = [];
168+ const info = notification.data as GradleCompatibilityInfo;
169+ const highestJavaVersion = Number(info.highestJavaVersion);
170+ let runtimes = await findRuntimes({checkJavac: true, withVersion: true, withTags: true});
171+ runtimes = runtimes.filter(runtime => {
172+ return runtime.version.major <= highestJavaVersion;
173+ });
174+ sortJdksByVersion(runtimes);
175+ sortJdksBySource(runtimes);
176+ options.push(UPGRADE_GRADLE + info.recommendedGradleVersion);
177+ if (!runtimes.length) {
178+ options.push(GET_JDK);
179+ } else {
180+ options.push(USE_JAVA + runtimes[0].version.major + AS_GRADLE_JVM);
181+ }
182+ this.showGradleCompatibilityIssueNotification(info.message, options, info.projectUri, runtimes[0]?.homedir);
160183 default:
161184 break;
162185 }
@@ -230,6 +253,44 @@ export class StandardLanguageClient {
230253 this.status = ClientStatus.Initialized;
231254 }
232255
256+ private showGradleCompatibilityIssueNotification(message: string, options: string[], projectUri: string, newJavaHome: string) {
257+ window.showErrorMessage(message + " [Learn More](https://docs.gradle.org/current/userguide/compatibility.html)", ...options).then(async (choice) => {
258+ if (choice === GET_JDK) {
259+ commands.executeCommand(Commands.OPEN_BROWSER, Uri.parse(getJdkUrl()));
260+ } else if (choice.startsWith(USE_JAVA)) {
261+ await workspace.getConfiguration().update(GRADLE_IMPORT_JVM, newJavaHome, ConfigurationTarget.Global);
262+ commands.executeCommand("workbench.action.openSettings", GRADLE_IMPORT_JVM);
263+ commands.executeCommand(Commands.IMPORT_PROJECTS);
264+ } else if (choice.startsWith(UPGRADE_GRADLE)) {
265+ const useWrapper = workspace.getConfiguration().get<boolean>("java.import.gradle.wrapper.enabled");
266+ if (!useWrapper) {
267+ await workspace.getConfiguration().update("java.import.gradle.wrapper.enabled", true, ConfigurationTarget.Workspace);
268+ }
269+ const result = await window.withProgress({
270+ location: ProgressLocation.Notification,
271+ title: "Upgrading Gradle wrapper...",
272+ cancellable: true,
273+ }, (_progress, token) => {
274+ return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, "java.project.upgradeGradle", projectUri, token);
275+ });
276+ if (result) {
277+ const propertiesFile = path.join(Uri.parse(projectUri).fsPath, "gradle", "wrapper", "gradle-wrapper.properties");
278+ if (fse.pathExists(propertiesFile)) {
279+ const content = await fse.readFile(propertiesFile);
280+ const offset = content.toString().indexOf("distributionUrl");
281+ if (offset >= 0) {
282+ const document = await workspace.openTextDocument(propertiesFile);
283+ const position = document.positionAt(offset);
284+ const distributionUrlRange = document.getWordRangeAtPosition(position);
285+ window.showTextDocument(document, {selection: new Range(distributionUrlRange.start, new Position(distributionUrlRange.start.line + 1, 0))});
286+ }
287+ }
288+ commands.executeCommand(Commands.IMPORT_PROJECTS);
289+ }
290+ }
291+ });
292+ }
293+
233294 private registerCommandsForStandardServer(context: ExtensionContext, jdtEventEmitter: EventEmitter<Uri>): void {
234295 context.subscriptions.push(commands.registerCommand(Commands.IMPORT_PROJECTS, async () => {
235296 return await commands.executeCommand<void>(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.IMPORT_PROJECTS);
0 commit comments