Skip to content

Commit 6c8fed7

Browse files
Add more debug entries via menu (#627)
* Add more debug entries via menu Signed-off-by: Jinbo Wang <[email protected]> * Make tslint happy Signed-off-by: Jinbo Wang <[email protected]> * Keep the menus only enabled in editor context Signed-off-by: Jinbo Wang <[email protected]> * Address review comment Signed-off-by: Jinbo Wang <[email protected]>
1 parent 3fce970 commit 6c8fed7

File tree

5 files changed

+190
-108
lines changed

5 files changed

+190
-108
lines changed

package.json

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
"onLanguage:java",
3535
"onDebugInitialConfigurations",
3636
"onDebugResolve:java",
37-
"onCommand:JavaDebug.SpecifyProgramArgs"
37+
"onCommand:JavaDebug.SpecifyProgramArgs",
38+
"onCommand:java.debug.runJavaFile",
39+
"onCommand:java.debug.debugJavaFile"
3840
],
3941
"main": "./dist/extension",
4042
"contributes": {
@@ -54,9 +56,41 @@
5456
"dark": "images/commands/hot_code_replace.svg",
5557
"light": "images/commands/hot_code_replace.svg"
5658
}
59+
},
60+
{
61+
"command": "java.debug.runJavaFile",
62+
"title": "Run"
63+
},
64+
{
65+
"command": "java.debug.debugJavaFile",
66+
"title": "Debug"
5767
}
5868
],
5969
"menus": {
70+
"explorer/context": [
71+
{
72+
"command": "java.debug.runJavaFile",
73+
"when": "resourceExtname == .java",
74+
"group": "javadebug@1"
75+
},
76+
{
77+
"command": "java.debug.debugJavaFile",
78+
"when": "resourceExtname == .java",
79+
"group": "javadebug@2"
80+
}
81+
],
82+
"editor/context": [
83+
{
84+
"command": "java.debug.runJavaFile",
85+
"when": "editorLangId == java && resourceExtname == .java",
86+
"group": "javadebug@1"
87+
},
88+
{
89+
"command": "java.debug.debugJavaFile",
90+
"when": "editorLangId == java && resourceExtname == .java",
91+
"group": "javadebug@2"
92+
}
93+
],
6094
"debug/toolBar": [
6195
{
6296
"command": "java.debug.hotCodeReplace",
@@ -68,6 +102,14 @@
68102
{
69103
"command": "java.debug.hotCodeReplace",
70104
"when": "false"
105+
},
106+
{
107+
"command": "java.debug.runJavaFile",
108+
"when": "false"
109+
},
110+
{
111+
"command": "java.debug.debugJavaFile",
112+
"when": "false"
71113
}
72114
]
73115
},

src/debugCodeLensProvider.ts

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
import * as _ from "lodash";
55
import * as vscode from "vscode";
6+
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
67

78
import { JAVA_LANGID } from "./constants";
89
import { IMainMethod, resolveMainMethod } from "./languageServerPlugin";
9-
import { logger, Type } from "./logger";
1010

11-
const JAVA_RUN_COMMAND = "vscode.java.run";
12-
const JAVA_DEBUG_COMMAND = "vscode.java.debug";
11+
const JAVA_RUN_CODELENS_COMMAND = "java.debug.runCodeLens";
12+
const JAVA_DEBUG_CODELENS_COMMAND = "java.debug.debugCodeLens";
1313
const JAVA_DEBUG_CONFIGURATION = "java.debug.settings";
1414
const ENABLE_CODE_LENS_VARIABLE = "enableRunDebugCodeLens";
1515

@@ -24,8 +24,8 @@ class DebugCodeLensContainer implements vscode.Disposable {
2424
private configurationEvent: vscode.Disposable;
2525

2626
constructor() {
27-
this.runCommand = vscode.commands.registerCommand(JAVA_RUN_COMMAND, runJavaProgram);
28-
this.debugCommand = vscode.commands.registerCommand(JAVA_DEBUG_COMMAND, debugJavaProgram);
27+
this.runCommand = instrumentOperationAsVsCodeCommand(JAVA_RUN_CODELENS_COMMAND, runJavaProgram);
28+
this.debugCommand = instrumentOperationAsVsCodeCommand(JAVA_DEBUG_CODELENS_COMMAND, debugJavaProgram);
2929

3030
const configuration = vscode.workspace.getConfiguration(JAVA_DEBUG_CONFIGURATION)
3131
const isCodeLensEnabled = configuration.get<boolean>(ENABLE_CODE_LENS_VARIABLE);
@@ -62,48 +62,37 @@ class DebugCodeLensContainer implements vscode.Disposable {
6262
class DebugCodeLensProvider implements vscode.CodeLensProvider {
6363

6464
public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
65-
const mainMethods: IMainMethod[] = await resolveMainMethod(document.uri);
66-
return _.flatten(mainMethods.map((method) => {
67-
return [
68-
new vscode.CodeLens(method.range, {
69-
title: "Run",
70-
command: JAVA_RUN_COMMAND,
71-
tooltip: "Run Java Program",
72-
arguments: [ method.mainClass, method.projectName, document.uri ],
73-
}),
74-
new vscode.CodeLens(method.range, {
75-
title: "Debug",
76-
command: JAVA_DEBUG_COMMAND,
77-
tooltip: "Debug Java Program",
78-
arguments: [ method.mainClass, method.projectName, document.uri ],
79-
}),
80-
];
81-
}));
65+
try {
66+
const mainMethods: IMainMethod[] = await resolveMainMethod(document.uri);
67+
return _.flatten(mainMethods.map((method) => {
68+
return [
69+
new vscode.CodeLens(method.range, {
70+
title: "Run",
71+
command: JAVA_RUN_CODELENS_COMMAND,
72+
tooltip: "Run Java Program",
73+
arguments: [ method.mainClass, method.projectName, document.uri ],
74+
}),
75+
new vscode.CodeLens(method.range, {
76+
title: "Debug",
77+
command: JAVA_DEBUG_CODELENS_COMMAND,
78+
tooltip: "Debug Java Program",
79+
arguments: [ method.mainClass, method.projectName, document.uri ],
80+
}),
81+
];
82+
}));
83+
} catch (ex) {
84+
// do nothing.
85+
return [];
86+
}
8287
}
8388
}
8489

85-
function runJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<void> {
86-
return runCodeLens(mainClass, projectName, uri, true);
90+
function runJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<boolean> {
91+
return startDebugging(mainClass, projectName, uri, true);
8792
}
8893

89-
function debugJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<void> {
90-
return runCodeLens(mainClass, projectName, uri, false);
91-
}
92-
93-
async function runCodeLens(mainClass: string, projectName: string, uri: vscode.Uri, noDebug: boolean): Promise<void> {
94-
const workspaceFolder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
95-
const workspaceUri: vscode.Uri = workspaceFolder ? workspaceFolder.uri : undefined;
96-
97-
const debugConfig: vscode.DebugConfiguration = await constructDebugConfig(mainClass, projectName, workspaceUri);
98-
debugConfig.projectName = projectName;
99-
debugConfig.noDebug = noDebug;
100-
101-
vscode.debug.startDebugging(workspaceFolder, debugConfig);
102-
103-
logger.log(Type.USAGEDATA, {
104-
runCodeLens: "yes",
105-
noDebug: String(noDebug),
106-
});
94+
function debugJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<boolean> {
95+
return startDebugging(mainClass, projectName, uri, false);
10796
}
10897

10998
async function constructDebugConfig(mainClass: string, projectName: string, workspace: vscode.Uri): Promise<vscode.DebugConfiguration> {
@@ -139,3 +128,14 @@ async function constructDebugConfig(mainClass: string, projectName: string, work
139128

140129
return _.cloneDeep(debugConfig);
141130
}
131+
132+
export async function startDebugging(mainClass: string, projectName: string, uri: vscode.Uri, noDebug: boolean): Promise<boolean> {
133+
const workspaceFolder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
134+
const workspaceUri: vscode.Uri = workspaceFolder ? workspaceFolder.uri : undefined;
135+
136+
const debugConfig: vscode.DebugConfiguration = await constructDebugConfig(mainClass, projectName, workspaceUri);
137+
debugConfig.projectName = projectName;
138+
debugConfig.noDebug = noDebug;
139+
140+
return vscode.debug.startDebugging(workspaceFolder, debugConfig);
141+
}

src/extension.ts

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Licensed under the MIT license.
33

44
import * as vscode from "vscode";
5-
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation } from "vscode-extension-telemetry-wrapper";
5+
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation,
6+
instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
67
import * as commands from "./commands";
78
import { JavaDebugConfigurationProvider } from "./configurationProvider";
89
import { HCR_EVENT, JAVA_LANGID, USER_NOTIFICATION_EVENT } from "./constants";
9-
import { initializeCodeLensProvider } from "./debugCodeLensProvider"
10+
import { initializeCodeLensProvider, startDebugging } from "./debugCodeLensProvider"
1011
import { handleHotCodeReplaceCustomEvent, initializeHotCodeReplace } from "./hotCodeReplace";
12+
import { IMainMethod, resolveMainMethod } from "./languageServerPlugin";
1113
import { logger, Type } from "./logger";
1214
import * as utility from "./utility";
1315

@@ -23,8 +25,31 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
2325
description: "activateExtension",
2426
});
2527

28+
registerDebugEventListener(context);
29+
context.subscriptions.push(logger);
30+
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));
31+
context.subscriptions.push(instrumentOperationAsVsCodeCommand("JavaDebug.SpecifyProgramArgs", async () => {
32+
return specifyProgramArguments(context);
33+
}));
34+
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.hotCodeReplace", applyHCR));
35+
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.runJavaFile", async (uri: vscode.Uri) => {
36+
await runJavaFile(uri, true);
37+
}));
38+
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.debugJavaFile", async (uri: vscode.Uri) => {
39+
await runJavaFile(uri, false);
40+
}));
41+
initializeHotCodeReplace(context);
42+
initializeCodeLensProvider(context);
43+
}
44+
45+
// this method is called when your extension is deactivated
46+
export async function deactivate() {
47+
await disposeTelemetryWrapper();
48+
}
49+
50+
function registerDebugEventListener(context: vscode.ExtensionContext) {
2651
const measureKeys = ["duration"];
27-
vscode.debug.onDidTerminateDebugSession((e) => {
52+
context.subscriptions.push(vscode.debug.onDidTerminateDebugSession((e) => {
2853
if (e.type !== "java") {
2954
return;
3055
}
@@ -44,45 +69,8 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
4469
});
4570
}
4671
});
47-
});
48-
49-
context.subscriptions.push(logger);
50-
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));
51-
context.subscriptions.push(instrumentAndRegisterCommand("JavaDebug.SpecifyProgramArgs", async () => {
52-
return specifyProgramArguments(context);
5372
}));
54-
context.subscriptions.push(instrumentAndRegisterCommand("java.debug.hotCodeReplace", async (args: any) => {
55-
const autobuildConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("java.autobuild");
56-
if (!autobuildConfig.enabled) {
57-
const ans = await vscode.window.showWarningMessage(
58-
"The hot code replace feature requires you to enable the autobuild flag, do you want to enable it?",
59-
"Yes", "No");
60-
if (ans === "Yes") {
61-
await autobuildConfig.update("enabled", true);
62-
// Force an incremental build to avoid auto build is not finishing during HCR.
63-
try {
64-
await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false)
65-
} catch (err) {
66-
// do nothing.
67-
}
68-
}
69-
}
7073

71-
const debugSession: vscode.DebugSession = vscode.debug.activeDebugSession;
72-
if (!debugSession) {
73-
return;
74-
}
75-
76-
return vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, async (progress) => {
77-
progress.report({ message: "Applying code changes..." });
78-
79-
const response = await debugSession.customRequest("redefineClasses");
80-
if (!response || !response.changedClasses || !response.changedClasses.length) {
81-
vscode.window.showWarningMessage("Cannot find any changed classes for hot replace!");
82-
}
83-
});
84-
}));
85-
initializeHotCodeReplace(context);
8674
context.subscriptions.push(vscode.debug.onDidReceiveDebugSessionCustomEvent((customEvent) => {
8775
const t = customEvent.session ? customEvent.session.type : undefined;
8876
if (t !== JAVA_LANGID) {
@@ -94,13 +82,6 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
9482
handleUserNotification(customEvent);
9583
}
9684
}));
97-
98-
initializeCodeLensProvider(context);
99-
}
100-
101-
// this method is called when your extension is deactivated
102-
export async function deactivate() {
103-
await disposeTelemetryWrapper();
10485
}
10586

10687
function handleUserNotification(customEvent) {
@@ -144,7 +125,69 @@ function specifyProgramArguments(context: vscode.ExtensionContext): Thenable<str
144125
});
145126
}
146127

147-
function instrumentAndRegisterCommand(name: string, cb: (...args: any[]) => any) {
148-
const instrumented = instrumentOperation(name, async (_operationId, myargs) => await cb(myargs));
149-
return vscode.commands.registerCommand(name, instrumented);
128+
async function applyHCR() {
129+
const autobuildConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("java.autobuild");
130+
if (!autobuildConfig.enabled) {
131+
const ans = await vscode.window.showWarningMessage(
132+
"The hot code replace feature requires you to enable the autobuild flag, do you want to enable it?",
133+
"Yes", "No");
134+
if (ans === "Yes") {
135+
await autobuildConfig.update("enabled", true);
136+
// Force an incremental build to avoid auto build is not finishing during HCR.
137+
try {
138+
await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false)
139+
} catch (err) {
140+
// do nothing.
141+
}
142+
}
143+
}
144+
145+
const debugSession: vscode.DebugSession = vscode.debug.activeDebugSession;
146+
if (!debugSession) {
147+
return;
148+
}
149+
150+
return vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, async (progress) => {
151+
progress.report({ message: "Applying code changes..." });
152+
153+
const response = await debugSession.customRequest("redefineClasses");
154+
if (!response || !response.changedClasses || !response.changedClasses.length) {
155+
vscode.window.showWarningMessage("Cannot find any changed classes for hot replace!");
156+
}
157+
});
158+
}
159+
160+
async function runJavaFile(uri: vscode.Uri, noDebug: boolean) {
161+
try {
162+
// Wait for Java Language Support extension being activated.
163+
await utility.getJavaExtensionAPI();
164+
} catch (ex) {
165+
if (ex instanceof utility.JavaExtensionNotActivatedError) {
166+
utility.guideToInstallJavaExtension();
167+
return;
168+
}
169+
170+
throw ex;
171+
}
172+
173+
const mainMethods: IMainMethod[] = await resolveMainMethod(uri);
174+
if (!mainMethods || !mainMethods.length) {
175+
vscode.window.showErrorMessage(
176+
"Error: Main method not found in the file, please define the main method as: public static void main(String[] args)");
177+
return;
178+
}
179+
180+
const projectName = mainMethods[0].projectName;
181+
let mainClass = mainMethods[0].mainClass;
182+
if (mainMethods.length > 1) {
183+
mainClass = await vscode.window.showQuickPick(mainMethods.map((mainMethod) => mainMethod.mainClass), {
184+
placeHolder: "Select the main class to launch.",
185+
});
186+
}
187+
188+
if (!mainClass) {
189+
return;
190+
}
191+
192+
await startDebugging(mainClass, projectName, uri, noDebug);
150193
}

src/languageServerPlugin.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ export interface ILaunchValidationResponse {
2929
}
3030

3131
export async function resolveMainMethod(uri: vscode.Uri): Promise<IMainMethod[]> {
32-
try {
33-
return <IMainMethod[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString());
34-
} catch (ex) {
35-
logger.log(Type.EXCEPTION, utility.formatErrorProperties(ex));
36-
return [];
37-
}
32+
return <IMainMethod[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString());
3833
}
3934

4035
export function startDebugSession() {

0 commit comments

Comments
 (0)