diff --git a/src/extension.ts b/src/extension.ts index 1525d5ab..ab8304d8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -264,13 +264,52 @@ async function runJavaFile(uri: vscode.Uri, noDebug: boolean) { throw ex; } + const hasMainMethods: boolean = mainMethods.length > 0; + const canRunTests: boolean = await canDelegateToJavaTestRunner(uri); + + if (!hasMainMethods && !canRunTests) { + const mainClasses: IMainClassOption[] = await utility.searchMainMethods(); + await launchMain(mainClasses, uri, noDebug); + } else if (hasMainMethods && !canRunTests) { + await launchMain(mainMethods, uri, noDebug); + } else if (!hasMainMethods && canRunTests) { + await launchTesting(uri, noDebug); + } else { + const launchMainChoice: string = "main() method"; + const launchTestChoice: string = "unit tests"; + const choice: string = await vscode.window.showQuickPick( + [launchMainChoice, launchTestChoice], + { placeHolder: "Please select which kind of task you would like to launch" }, + ); + if (choice === launchMainChoice) { + await launchMain(mainMethods, uri, noDebug); + } else if (choice === launchTestChoice) { + await launchTesting(uri, noDebug); + } + } +} + +async function canDelegateToJavaTestRunner(uri: vscode.Uri): Promise { + const fsPath: string = uri.fsPath; + const isTestFile: boolean = /.*[\/\\]src[\/\\]test[\/\\]java[\/\\].*[Tt]ests?\.java/.test(fsPath); + if (!isTestFile) { + return false; + } + return (await vscode.commands.getCommands()).includes("java.test.editor.run"); +} + +async function launchTesting(uri: vscode.Uri, noDebug: boolean): Promise { + noDebug ? vscode.commands.executeCommand("java.test.editor.run", uri) : vscode.commands.executeCommand("java.test.editor.debug", uri); +} + +async function launchMain(mainMethods: IMainClassOption[], uri: vscode.Uri, noDebug: boolean): Promise { if (!mainMethods || !mainMethods.length) { vscode.window.showErrorMessage( "Error: Main method not found in the file, please define the main method as: public static void main(String[] args)"); return; } - const pick = await mainClassPicker.showQuickPick(mainMethods, "Select the main class to run.", (option) => option.mainClass); + const pick = await mainClassPicker.showQuickPickWithRecentlyUsed(mainMethods, "Select the main class to run."); if (!pick) { return; } @@ -287,23 +326,7 @@ async function runJavaProject(node: any, noDebug: boolean) { throw error; } - let mainClassesOptions: IMainClassOption[] = []; - try { - mainClassesOptions = await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Window, - }, - async (p) => { - p.report({ - message: "Searching main class...", - }); - return resolveMainClass(vscode.Uri.parse(node.uri)); - }); - } catch (ex) { - vscode.window.showErrorMessage(String((ex && ex.message) || ex)); - throw ex; - } - + const mainClassesOptions: IMainClassOption[] = await utility.searchMainMethods(vscode.Uri.parse(node.uri)); if (!mainClassesOptions || !mainClassesOptions.length) { vscode.window.showErrorMessage(`Failed to ${noDebug ? "run" : "debug"} this project '${node._nodeData.displayName || node.name}' ` + "because it does not contain any main class."); @@ -311,7 +334,7 @@ async function runJavaProject(node: any, noDebug: boolean) { } const pick = await mainClassPicker.showQuickPickWithRecentlyUsed(mainClassesOptions, - "Select the main class to run.", (option) => option.mainClass); + "Select the main class to run."); if (!pick) { return; } diff --git a/src/mainClassPicker.ts b/src/mainClassPicker.ts index 43a3deaf..fa017ea1 100644 --- a/src/mainClassPicker.ts +++ b/src/mainClassPicker.ts @@ -7,7 +7,7 @@ import { TextEditor, window } from "vscode"; import { IMainClassOption } from "./languageServerPlugin"; const defaultLabelFormatter = (option: IMainClassOption) => { - return option.mainClass + `${option.projectName ? "<" + option.projectName + ">" : ""}`; + return option.mainClass; }; type Formatter = (option: IMainClassOption) => string; @@ -44,7 +44,7 @@ class MainClassPicker { return { label: labelFormatter(option), description: option.filePath ? path.basename(option.filePath) : "", - detail: undefined, + detail: option.projectName ? `Project: ${option.projectName}` : "", data: option, }; }); @@ -109,6 +109,10 @@ class MainClassPicker { adjustedDetail.push(`$(file-text) active editor (${path.basename(option.filePath)})`); } + if (option.projectName) { + adjustedDetail.push(`Project: ${option.projectName}`); + } + const detail: string = adjustedDetail.join(", "); return { diff --git a/src/utility.ts b/src/utility.ts index f8fb8d01..0b24e364 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -4,6 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper"; +import { IMainClassOption, resolveMainClass } from "./languageServerPlugin"; import { logger, Type } from "./logger"; const TROUBLESHOOTING_LINK = "https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md"; @@ -248,3 +249,17 @@ export async function waitForStandardMode(): Promise { return true; } + +export async function searchMainMethods(uri?: vscode.Uri): Promise { + try { + return await vscode.window.withProgress( + { location: vscode.ProgressLocation.Window }, + async (p) => { + p.report({ message: "Searching main classes..." }); + return resolveMainClass(uri); + }); + } catch (ex) { + vscode.window.showErrorMessage(String((ex && ex.message) || ex)); + throw ex; + } +}