From b2295823baab8573b317cc4ec078c22b19293219 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Wed, 28 Oct 2020 16:14:58 +0800 Subject: [PATCH 1/6] Make the run buttons in editor toolbar more context-aware - If the file has a main() method, run it - If the file is a test file, delegate to Test Runner - If neither has a main() method nor is a test file, try to launch as a project - If both has a main() method and is a test file, let the user to choose Signed-off-by: Sheng Chen --- src/extension.ts | 62 +++++++++++++++++++++++++++++++++++------------- src/utility.ts | 19 +++++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1525d5ab..ab31f5d6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -264,6 +264,50 @@ 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 workspaceFolder: vscode.WorkspaceFolder | undefined = vscode.workspace.getWorkspaceFolder(uri); + if (!workspaceFolder) { + vscode.window.showErrorMessage(`Failed to run the project because the file: ${uri.fsPath} does not belongs to the current workspace.`); + return; + } + const mainClasses: IMainClassOption[] = await utility.searchMainMethods(workspaceFolder.uri); + 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)"); @@ -287,23 +331,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."); diff --git a/src/utility.ts b/src/utility.ts index f8fb8d01..434a53ec 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,21 @@ 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 class...", + }); + return resolveMainClass(uri); + }); + } catch (ex) { + vscode.window.showErrorMessage(String((ex && ex.message) || ex)); + throw ex; + } +} From 495239aabe2539d5b9270f1ea64994f7e7fb4443 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Wed, 28 Oct 2020 16:23:41 +0800 Subject: [PATCH 2/6] fix build error Signed-off-by: Sheng Chen --- src/utility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility.ts b/src/utility.ts index 434a53ec..d1dda798 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -250,7 +250,7 @@ export async function waitForStandardMode(): Promise { return true; } -export async function searchMainMethods(uri: vscode.Uri): Promise { +export async function searchMainMethods(uri: vscode.Uri): Promise { try { return await vscode.window.withProgress( { From 04ba9fbbffd6baca8393396eeffbbbbfae481fbf Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 30 Oct 2020 11:09:47 +0800 Subject: [PATCH 3/6] Can search the main classes in the whole workspace Signed-off-by: Sheng Chen --- src/extension.ts | 7 +------ src/utility.ts | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ab31f5d6..11f375e4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -268,12 +268,7 @@ async function runJavaFile(uri: vscode.Uri, noDebug: boolean) { const canRunTests: boolean = await canDelegateToJavaTestRunner(uri); if (!hasMainMethods && !canRunTests) { - const workspaceFolder: vscode.WorkspaceFolder | undefined = vscode.workspace.getWorkspaceFolder(uri); - if (!workspaceFolder) { - vscode.window.showErrorMessage(`Failed to run the project because the file: ${uri.fsPath} does not belongs to the current workspace.`); - return; - } - const mainClasses: IMainClassOption[] = await utility.searchMainMethods(workspaceFolder.uri); + const mainClasses: IMainClassOption[] = await utility.searchMainMethods(); await launchMain(mainClasses, uri, noDebug); } else if (hasMainMethods && !canRunTests) { await launchMain(mainMethods, uri, noDebug); diff --git a/src/utility.ts b/src/utility.ts index d1dda798..245adce2 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import * as _ from "lodash"; import * as path from "path"; import * as vscode from "vscode"; import { sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper"; @@ -250,17 +251,20 @@ export async function waitForStandardMode(): Promise { return true; } -export async function searchMainMethods(uri: vscode.Uri): Promise { +export async function searchMainMethods(...uris: vscode.Uri[] | undefined): Promise { try { return await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Window, - }, + { location: vscode.ProgressLocation.Window }, async (p) => { - p.report({ - message: "Searching main class...", - }); - return resolveMainClass(uri); + p.report({ message: "Searching main classes..." }); + if (_.isEmpty(uris)) { + uris = vscode.workspace.workspaceFolders.map((folder) => folder.uri); + } + const mainClasses: IMainClassOption[] = []; + for (const uri of uris) { + mainClasses.push(...await resolveMainClass(uri)); + } + return mainClasses; }); } catch (ex) { vscode.window.showErrorMessage(String((ex && ex.message) || ex)); From c537c0b9a2894de4a5112fe4022df6b4b5192bf7 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 30 Oct 2020 14:00:39 +0800 Subject: [PATCH 4/6] Show project name as the option detail Signed-off-by: Sheng Chen --- src/extension.ts | 4 ++-- src/mainClassPicker.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 11f375e4..ab8304d8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -309,7 +309,7 @@ async function launchMain(mainMethods: IMainClassOption[], uri: vscode.Uri, noDe 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; } @@ -334,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 { From 3d677fc984ad293eca06ca07411fc97620e7a2d4 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 30 Oct 2020 15:04:40 +0800 Subject: [PATCH 5/6] Address the comments --- src/utility.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/utility.ts b/src/utility.ts index 245adce2..a90fb37f 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -251,20 +251,13 @@ export async function waitForStandardMode(): Promise { return true; } -export async function searchMainMethods(...uris: vscode.Uri[] | undefined): Promise { +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..." }); - if (_.isEmpty(uris)) { - uris = vscode.workspace.workspaceFolders.map((folder) => folder.uri); - } - const mainClasses: IMainClassOption[] = []; - for (const uri of uris) { - mainClasses.push(...await resolveMainClass(uri)); - } - return mainClasses; + return resolveMainClass(uri); }); } catch (ex) { vscode.window.showErrorMessage(String((ex && ex.message) || ex)); From cd1e7aee780cecc0ce69d03feb7ca58fc544da1b Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 30 Oct 2020 15:06:26 +0800 Subject: [PATCH 6/6] Remove unused import --- src/utility.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utility.ts b/src/utility.ts index a90fb37f..0b24e364 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import * as _ from "lodash"; import * as path from "path"; import * as vscode from "vscode"; import { sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper";