diff --git a/src/build.ts b/src/build.ts new file mode 100644 index 00000000..d4c56749 --- /dev/null +++ b/src/build.ts @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +import * as path from "path"; +import * as vscode from "vscode"; +import { sendError, setErrorCode, setUserError } from "vscode-extension-telemetry-wrapper"; + +import * as anchor from "./anchor"; +import * as commands from "./commands"; +import * as lsPlugin from "./languageServerPlugin"; +import * as utility from "./utility"; + +export async function buildWorkspace(): Promise { + try { + await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false); + } catch (err) { + return handleBuildFailure(err); + } + + return true; +} + +async function handleBuildFailure(err: any): Promise { + if (err instanceof utility.JavaExtensionNotActivatedError) { + utility.guideToInstallJavaExtension(); + return false; + } + + const error: Error = new utility.UserError({ + message: "Build failed", + }); + setErrorCode(error, Number(err)); + sendError(error); + + if (err === lsPlugin.CompileWorkspaceStatus.WITHERROR || err === lsPlugin.CompileWorkspaceStatus.FAILED) { + if (checkErrorsReportedByJavaExtension()) { + vscode.commands.executeCommand("workbench.actions.view.problems"); + } + + const ans = await vscode.window.showErrorMessage("Build failed, do you want to continue?", + "Proceed", "Fix...", "Cancel"); + if (ans === "Proceed") { + return true; + } else if (ans === "Fix...") { + showFixSuggestions(); + } + + return false; + } + + return true; +} + +function checkErrorsReportedByJavaExtension(): boolean { + const problems = vscode.languages.getDiagnostics() || []; + for (const problem of problems) { + const fileName = path.basename(problem[0].fsPath || ""); + if (fileName.endsWith(".java") || fileName === "pom.xml" || fileName.endsWith(".gradle")) { + if (problem[1].filter((diagnostic) => diagnostic.severity === vscode.DiagnosticSeverity.Error).length) { + return true; + } + } + } + + return false; +} + +async function showFixSuggestions() { + let buildFiles = []; + try { + buildFiles = await lsPlugin.resolveBuildFiles(); + } catch (error) { + // do nothing + } + + const pickitems = []; + pickitems.push({ + label: "Clean workspace cache", + detail: "Clean the stale workspace and reload the window", + }); + if (buildFiles.length) { + pickitems.push({ + label: "Update project configuration", + detail: "Force the language server to update the project configuration/classpath", + }); + } + pickitems.push({ + label: "Troubleshooting guide", + detail: "Find more detail about the troubleshooting steps", + }); + + const ans = await vscode.window.showQuickPick(pickitems, { + placeHolder: "Please fix the errors in PROBLEMS first, then try the fix suggestions below.", + }); + if (ans.label === "Clean workspace cache") { + vscode.commands.executeCommand("java.clean.workspace"); + } else if (ans.label === "Update project configuration") { + for (const buildFile of buildFiles) { + await vscode.commands.executeCommand("java.projectConfiguration.update", vscode.Uri.parse(buildFile)); + } + } else if (ans.label === "Troubleshooting guide") { + utility.openTroubleshootingPage("Build failed", anchor.BUILD_FAILED); + } +} diff --git a/src/commands.ts b/src/commands.ts index e1a93e3f..81636136 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -32,6 +32,8 @@ export const JAVA_CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; export const JAVA_RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; +export const JAVA_RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; + export function executeJavaLanguageServerCommand(...rest) { // TODO: need to handle error and trace telemetry if (!utility.isJavaExtEnabled()) { diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index fd8ae173..110f0906 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -8,6 +8,7 @@ import * as vscode from "vscode"; import { instrumentOperation } from "vscode-extension-telemetry-wrapper"; import * as anchor from "./anchor"; +import { buildWorkspace } from "./build"; import * as commands from "./commands"; import * as lsPlugin from "./languageServerPlugin"; import { detectLaunchCommandStyle } from "./launchCommand"; @@ -163,22 +164,9 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } if (needsBuildWorkspace()) { - try { - const buildResult = await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false); - } catch (err) { - if (err instanceof utility.JavaExtensionNotActivatedError) { - utility.guideToInstallJavaExtension(); - return undefined; - } - - const ans = await utility.showErrorMessageWithTroubleshooting({ - message: "Build failed, do you want to continue?", - type: Type.USAGEERROR, - anchor: anchor.BUILD_FAILED, - }, "Proceed", "Abort"); - if (ans !== "Proceed") { - return undefined; - } + const proceed = await buildWorkspace(); + if (!proceed) { + return undefined; } } diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts index 9d67f0a3..8b640c66 100644 --- a/src/languageServerPlugin.ts +++ b/src/languageServerPlugin.ts @@ -5,6 +5,13 @@ import * as vscode from "vscode"; import * as commands from "./commands"; +export enum CompileWorkspaceStatus { + FAILED = 0, + SUCCEED = 1, + WITHERROR = 2, + CANCELLED = 3, +} + export interface IMainClassOption { readonly mainClass: string; readonly projectName?: string; @@ -77,3 +84,7 @@ export async function detectPreviewFlag(className: string, projectName: string): export function resolveElementAtSelection(uri: string, line: number, character: number): Promise { return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_ELEMENT_AT_SELECTION, uri, line, character); } + +export function resolveBuildFiles(): Promise { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_BUILD_FILES); +} diff --git a/src/utility.ts b/src/utility.ts index cdc29fde..66c4dc80 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -86,17 +86,21 @@ export async function showErrorMessageWithTroubleshooting(message: ITroubleshoot function handleTroubleshooting(choice: string, message: string, anchor: string): string | undefined { if (choice === LEARN_MORE) { - vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); - logger.log(Type.USAGEDATA, { - troubleshooting: "yes", - troubleshootingMessage: message, - }); + openTroubleshootingPage(message, anchor); return; } return choice; } +export function openTroubleshootingPage(message: string, anchor: string) { + vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); + logger.log(Type.USAGEDATA, { + troubleshooting: "yes", + troubleshootingMessage: message, + }); +} + export async function guideToInstallJavaExtension() { const MESSAGE = "Language Support for Java is required. Please install and enable it."; const INSTALL = "Install";