Skip to content

Commit 0d35b05

Browse files
authored
Check Gradle compatibility when importing fails (#2245)
Signed-off-by: Shi Chen <[email protected]>
1 parent e8d1c40 commit 0d35b05

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

src/protocol.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export enum FeatureStatus {
5858
export enum EventType {
5959
ClasspathUpdated = 100,
6060
ProjectsImported = 200,
61+
IncompatibleGradleJdkIssue = 300,
6162
}
6263

6364
export enum CompileWorkspaceStatus {
@@ -406,3 +407,10 @@ export interface RenameFilesParams {
406407
export namespace WillRenameFiles {
407408
export const type = new RequestType<RenameFilesParams, WorkspaceEdit, void>('workspace/willRenameFiles');
408409
}
410+
411+
export interface GradleCompatibilityInfo {
412+
projectUri: string;
413+
message: string;
414+
highestJavaVersion: string;
415+
recommendedGradleVersion: string;
416+
}

src/requirements.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async function findDefaultRuntimeFromSettings(): Promise<string | undefined> {
147147
return undefined;
148148
}
149149

150-
function sortJdksBySource(jdks: IJavaRuntime[]) {
150+
export function sortJdksBySource(jdks: IJavaRuntime[]) {
151151
const rankedJdks = jdks as Array<IJavaRuntime & { rank: number }>;
152152
const sources = ["JDK_HOME", "JAVA_HOME", "PATH"];
153153
for (const [index, source] of sources.entries()) {
@@ -164,7 +164,7 @@ function sortJdksBySource(jdks: IJavaRuntime[]) {
164164
/**
165165
* Sort by major version in descend order.
166166
*/
167-
function sortJdksByVersion(jdks: IJavaRuntime[]) {
167+
export function sortJdksByVersion(jdks: IJavaRuntime[]) {
168168
jdks.sort((a, b) => (b.version?.major ?? 0) - (a.version?.major ?? 0));
169169
}
170170

@@ -196,7 +196,7 @@ function openJDKDownload(reject, cause) {
196196
});
197197
}
198198

199-
function getJdkUrl() {
199+
export function getJdkUrl() {
200200
let jdkUrl = 'https://developers.redhat.com/products/openjdk/download/?sc_cid=701f2000000RWTnAAO';
201201
if (process.platform === 'darwin') {
202202
jdkUrl = 'https://adoptopenjdk.net/';

src/standardLanguageClient.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
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";
44
import { Commands } from "./commands";
55
import { serverStatus, ServerStatusKind } from "./serverStatus";
66
import { prepareExecutable, awaitServerConnection } from "./javaServerStarter";
77
import { getJavaConfig, applyWorkspaceEdit } from "./extension";
88
import { LanguageClientOptions, Position as LSPosition, Location as LSLocation, MessageType, TextDocumentPositionParams, ConfigurationRequest, ConfigurationParams } from "vscode-languageclient";
99
import { 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";
1111
import { setGradleWrapperChecksum, excludeProjectSettingsFiles, ServerMode } from "./settings";
1212
import { onExtensionChange, collectBuildFilePattern } from "./plugin";
1313
import { activationProgressNotification, serverTaskPresenter } from "./serverTaskPresenter";
14-
import { RequirementsData } from "./requirements";
14+
import { getJdkUrl, RequirementsData, sortJdksBySource, sortJdksByVersion } from "./requirements";
1515
import * as net from 'net';
1616
import * as fse from 'fs-extra';
1717
import * as path from 'path';
@@ -32,9 +32,15 @@ import { typeHierarchyTree } from "./typeHierarchy/typeHierarchyTree";
3232
import { TypeHierarchyDirection, TypeHierarchyItem } from "./typeHierarchy/protocol";
3333
import { buildFilePatterns } from './plugin';
3434
import { pomCodeActionMetadata, PomCodeActionProvider } from "./pom/pomCodeActionProvider";
35+
import { findRuntimes, IJavaRuntime } from "jdk-utils";
3536

3637
const extensionName = 'Language Support for Java';
3738
const 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

3945
export 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

Comments
 (0)