Skip to content

Commit ce265cb

Browse files
authored
Make the run buttons in editor toolbar more context-aware (#898)
* 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 <[email protected]> * fix build error Signed-off-by: Sheng Chen <[email protected]> * Can search the main classes in the whole workspace Signed-off-by: Sheng Chen <[email protected]> * Show project name as the option detail Signed-off-by: Sheng Chen <[email protected]> * Address the comments * Remove unused import
1 parent 0f6ad59 commit ce265cb

File tree

3 files changed

+63
-21
lines changed

3 files changed

+63
-21
lines changed

src/extension.ts

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,52 @@ async function runJavaFile(uri: vscode.Uri, noDebug: boolean) {
264264
throw ex;
265265
}
266266

267+
const hasMainMethods: boolean = mainMethods.length > 0;
268+
const canRunTests: boolean = await canDelegateToJavaTestRunner(uri);
269+
270+
if (!hasMainMethods && !canRunTests) {
271+
const mainClasses: IMainClassOption[] = await utility.searchMainMethods();
272+
await launchMain(mainClasses, uri, noDebug);
273+
} else if (hasMainMethods && !canRunTests) {
274+
await launchMain(mainMethods, uri, noDebug);
275+
} else if (!hasMainMethods && canRunTests) {
276+
await launchTesting(uri, noDebug);
277+
} else {
278+
const launchMainChoice: string = "main() method";
279+
const launchTestChoice: string = "unit tests";
280+
const choice: string = await vscode.window.showQuickPick(
281+
[launchMainChoice, launchTestChoice],
282+
{ placeHolder: "Please select which kind of task you would like to launch" },
283+
);
284+
if (choice === launchMainChoice) {
285+
await launchMain(mainMethods, uri, noDebug);
286+
} else if (choice === launchTestChoice) {
287+
await launchTesting(uri, noDebug);
288+
}
289+
}
290+
}
291+
292+
async function canDelegateToJavaTestRunner(uri: vscode.Uri): Promise<boolean> {
293+
const fsPath: string = uri.fsPath;
294+
const isTestFile: boolean = /.*[\/\\]src[\/\\]test[\/\\]java[\/\\].*[Tt]ests?\.java/.test(fsPath);
295+
if (!isTestFile) {
296+
return false;
297+
}
298+
return (await vscode.commands.getCommands()).includes("java.test.editor.run");
299+
}
300+
301+
async function launchTesting(uri: vscode.Uri, noDebug: boolean): Promise<void> {
302+
noDebug ? vscode.commands.executeCommand("java.test.editor.run", uri) : vscode.commands.executeCommand("java.test.editor.debug", uri);
303+
}
304+
305+
async function launchMain(mainMethods: IMainClassOption[], uri: vscode.Uri, noDebug: boolean): Promise<void> {
267306
if (!mainMethods || !mainMethods.length) {
268307
vscode.window.showErrorMessage(
269308
"Error: Main method not found in the file, please define the main method as: public static void main(String[] args)");
270309
return;
271310
}
272311

273-
const pick = await mainClassPicker.showQuickPick(mainMethods, "Select the main class to run.", (option) => option.mainClass);
312+
const pick = await mainClassPicker.showQuickPickWithRecentlyUsed(mainMethods, "Select the main class to run.");
274313
if (!pick) {
275314
return;
276315
}
@@ -287,31 +326,15 @@ async function runJavaProject(node: any, noDebug: boolean) {
287326
throw error;
288327
}
289328

290-
let mainClassesOptions: IMainClassOption[] = [];
291-
try {
292-
mainClassesOptions = await vscode.window.withProgress<IMainClassOption[]>(
293-
{
294-
location: vscode.ProgressLocation.Window,
295-
},
296-
async (p) => {
297-
p.report({
298-
message: "Searching main class...",
299-
});
300-
return resolveMainClass(vscode.Uri.parse(node.uri));
301-
});
302-
} catch (ex) {
303-
vscode.window.showErrorMessage(String((ex && ex.message) || ex));
304-
throw ex;
305-
}
306-
329+
const mainClassesOptions: IMainClassOption[] = await utility.searchMainMethods(vscode.Uri.parse(node.uri));
307330
if (!mainClassesOptions || !mainClassesOptions.length) {
308331
vscode.window.showErrorMessage(`Failed to ${noDebug ? "run" : "debug"} this project '${node._nodeData.displayName || node.name}' `
309332
+ "because it does not contain any main class.");
310333
return;
311334
}
312335

313336
const pick = await mainClassPicker.showQuickPickWithRecentlyUsed(mainClassesOptions,
314-
"Select the main class to run.", (option) => option.mainClass);
337+
"Select the main class to run.");
315338
if (!pick) {
316339
return;
317340
}

src/mainClassPicker.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { TextEditor, window } from "vscode";
77
import { IMainClassOption } from "./languageServerPlugin";
88

99
const defaultLabelFormatter = (option: IMainClassOption) => {
10-
return option.mainClass + `${option.projectName ? "<" + option.projectName + ">" : ""}`;
10+
return option.mainClass;
1111
};
1212
type Formatter = (option: IMainClassOption) => string;
1313

@@ -44,7 +44,7 @@ class MainClassPicker {
4444
return {
4545
label: labelFormatter(option),
4646
description: option.filePath ? path.basename(option.filePath) : "",
47-
detail: undefined,
47+
detail: option.projectName ? `Project: ${option.projectName}` : "",
4848
data: option,
4949
};
5050
});
@@ -109,6 +109,10 @@ class MainClassPicker {
109109
adjustedDetail.push(`$(file-text) active editor (${path.basename(option.filePath)})`);
110110
}
111111

112+
if (option.projectName) {
113+
adjustedDetail.push(`Project: ${option.projectName}`);
114+
}
115+
112116
const detail: string = adjustedDetail.join(", ");
113117

114118
return {

src/utility.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import * as path from "path";
55
import * as vscode from "vscode";
66
import { sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper";
7+
import { IMainClassOption, resolveMainClass } from "./languageServerPlugin";
78
import { logger, Type } from "./logger";
89

910
const TROUBLESHOOTING_LINK = "https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md";
@@ -248,3 +249,17 @@ export async function waitForStandardMode(): Promise<boolean> {
248249

249250
return true;
250251
}
252+
253+
export async function searchMainMethods(uri?: vscode.Uri): Promise<IMainClassOption[]> {
254+
try {
255+
return await vscode.window.withProgress<IMainClassOption[]>(
256+
{ location: vscode.ProgressLocation.Window },
257+
async (p) => {
258+
p.report({ message: "Searching main classes..." });
259+
return resolveMainClass(uri);
260+
});
261+
} catch (ex) {
262+
vscode.window.showErrorMessage(String((ex && ex.message) || ex));
263+
throw ex;
264+
}
265+
}

0 commit comments

Comments
 (0)