Add more debug entries via menu (#627)
* Add more debug entries via menu Signed-off-by: Jinbo Wang <jinbwan@microsoft.com> * Make tslint happy Signed-off-by: Jinbo Wang <jinbwan@microsoft.com> * Keep the menus only enabled in editor context Signed-off-by: Jinbo Wang <jinbwan@microsoft.com> * Address review comment Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
This commit is contained in:
parent
3fce970039
commit
6c8fed7a99
44
package.json
44
package.json
|
@ -34,7 +34,9 @@
|
|||
"onLanguage:java",
|
||||
"onDebugInitialConfigurations",
|
||||
"onDebugResolve:java",
|
||||
"onCommand:JavaDebug.SpecifyProgramArgs"
|
||||
"onCommand:JavaDebug.SpecifyProgramArgs",
|
||||
"onCommand:java.debug.runJavaFile",
|
||||
"onCommand:java.debug.debugJavaFile"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
"contributes": {
|
||||
|
@ -54,9 +56,41 @@
|
|||
"dark": "images/commands/hot_code_replace.svg",
|
||||
"light": "images/commands/hot_code_replace.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "java.debug.runJavaFile",
|
||||
"title": "Run"
|
||||
},
|
||||
{
|
||||
"command": "java.debug.debugJavaFile",
|
||||
"title": "Debug"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"explorer/context": [
|
||||
{
|
||||
"command": "java.debug.runJavaFile",
|
||||
"when": "resourceExtname == .java",
|
||||
"group": "javadebug@1"
|
||||
},
|
||||
{
|
||||
"command": "java.debug.debugJavaFile",
|
||||
"when": "resourceExtname == .java",
|
||||
"group": "javadebug@2"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "java.debug.runJavaFile",
|
||||
"when": "editorLangId == java && resourceExtname == .java",
|
||||
"group": "javadebug@1"
|
||||
},
|
||||
{
|
||||
"command": "java.debug.debugJavaFile",
|
||||
"when": "editorLangId == java && resourceExtname == .java",
|
||||
"group": "javadebug@2"
|
||||
}
|
||||
],
|
||||
"debug/toolBar": [
|
||||
{
|
||||
"command": "java.debug.hotCodeReplace",
|
||||
|
@ -68,6 +102,14 @@
|
|||
{
|
||||
"command": "java.debug.hotCodeReplace",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "java.debug.runJavaFile",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "java.debug.debugJavaFile",
|
||||
"when": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
import * as _ from "lodash";
|
||||
import * as vscode from "vscode";
|
||||
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
|
||||
|
||||
import { JAVA_LANGID } from "./constants";
|
||||
import { IMainMethod, resolveMainMethod } from "./languageServerPlugin";
|
||||
import { logger, Type } from "./logger";
|
||||
|
||||
const JAVA_RUN_COMMAND = "vscode.java.run";
|
||||
const JAVA_DEBUG_COMMAND = "vscode.java.debug";
|
||||
const JAVA_RUN_CODELENS_COMMAND = "java.debug.runCodeLens";
|
||||
const JAVA_DEBUG_CODELENS_COMMAND = "java.debug.debugCodeLens";
|
||||
const JAVA_DEBUG_CONFIGURATION = "java.debug.settings";
|
||||
const ENABLE_CODE_LENS_VARIABLE = "enableRunDebugCodeLens";
|
||||
|
||||
|
@ -24,8 +24,8 @@ class DebugCodeLensContainer implements vscode.Disposable {
|
|||
private configurationEvent: vscode.Disposable;
|
||||
|
||||
constructor() {
|
||||
this.runCommand = vscode.commands.registerCommand(JAVA_RUN_COMMAND, runJavaProgram);
|
||||
this.debugCommand = vscode.commands.registerCommand(JAVA_DEBUG_COMMAND, debugJavaProgram);
|
||||
this.runCommand = instrumentOperationAsVsCodeCommand(JAVA_RUN_CODELENS_COMMAND, runJavaProgram);
|
||||
this.debugCommand = instrumentOperationAsVsCodeCommand(JAVA_DEBUG_CODELENS_COMMAND, debugJavaProgram);
|
||||
|
||||
const configuration = vscode.workspace.getConfiguration(JAVA_DEBUG_CONFIGURATION)
|
||||
const isCodeLensEnabled = configuration.get<boolean>(ENABLE_CODE_LENS_VARIABLE);
|
||||
|
@ -62,48 +62,37 @@ class DebugCodeLensContainer implements vscode.Disposable {
|
|||
class DebugCodeLensProvider implements vscode.CodeLensProvider {
|
||||
|
||||
public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
|
||||
const mainMethods: IMainMethod[] = await resolveMainMethod(document.uri);
|
||||
return _.flatten(mainMethods.map((method) => {
|
||||
return [
|
||||
new vscode.CodeLens(method.range, {
|
||||
title: "Run",
|
||||
command: JAVA_RUN_COMMAND,
|
||||
tooltip: "Run Java Program",
|
||||
arguments: [ method.mainClass, method.projectName, document.uri ],
|
||||
}),
|
||||
new vscode.CodeLens(method.range, {
|
||||
title: "Debug",
|
||||
command: JAVA_DEBUG_COMMAND,
|
||||
tooltip: "Debug Java Program",
|
||||
arguments: [ method.mainClass, method.projectName, document.uri ],
|
||||
}),
|
||||
];
|
||||
}));
|
||||
try {
|
||||
const mainMethods: IMainMethod[] = await resolveMainMethod(document.uri);
|
||||
return _.flatten(mainMethods.map((method) => {
|
||||
return [
|
||||
new vscode.CodeLens(method.range, {
|
||||
title: "Run",
|
||||
command: JAVA_RUN_CODELENS_COMMAND,
|
||||
tooltip: "Run Java Program",
|
||||
arguments: [ method.mainClass, method.projectName, document.uri ],
|
||||
}),
|
||||
new vscode.CodeLens(method.range, {
|
||||
title: "Debug",
|
||||
command: JAVA_DEBUG_CODELENS_COMMAND,
|
||||
tooltip: "Debug Java Program",
|
||||
arguments: [ method.mainClass, method.projectName, document.uri ],
|
||||
}),
|
||||
];
|
||||
}));
|
||||
} catch (ex) {
|
||||
// do nothing.
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<void> {
|
||||
return runCodeLens(mainClass, projectName, uri, true);
|
||||
function runJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<boolean> {
|
||||
return startDebugging(mainClass, projectName, uri, true);
|
||||
}
|
||||
|
||||
function debugJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<void> {
|
||||
return runCodeLens(mainClass, projectName, uri, false);
|
||||
}
|
||||
|
||||
async function runCodeLens(mainClass: string, projectName: string, uri: vscode.Uri, noDebug: boolean): Promise<void> {
|
||||
const workspaceFolder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
||||
const workspaceUri: vscode.Uri = workspaceFolder ? workspaceFolder.uri : undefined;
|
||||
|
||||
const debugConfig: vscode.DebugConfiguration = await constructDebugConfig(mainClass, projectName, workspaceUri);
|
||||
debugConfig.projectName = projectName;
|
||||
debugConfig.noDebug = noDebug;
|
||||
|
||||
vscode.debug.startDebugging(workspaceFolder, debugConfig);
|
||||
|
||||
logger.log(Type.USAGEDATA, {
|
||||
runCodeLens: "yes",
|
||||
noDebug: String(noDebug),
|
||||
});
|
||||
function debugJavaProgram(mainClass: string, projectName: string, uri: vscode.Uri): Promise<boolean> {
|
||||
return startDebugging(mainClass, projectName, uri, false);
|
||||
}
|
||||
|
||||
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
|
|||
|
||||
return _.cloneDeep(debugConfig);
|
||||
}
|
||||
|
||||
export async function startDebugging(mainClass: string, projectName: string, uri: vscode.Uri, noDebug: boolean): Promise<boolean> {
|
||||
const workspaceFolder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
||||
const workspaceUri: vscode.Uri = workspaceFolder ? workspaceFolder.uri : undefined;
|
||||
|
||||
const debugConfig: vscode.DebugConfiguration = await constructDebugConfig(mainClass, projectName, workspaceUri);
|
||||
debugConfig.projectName = projectName;
|
||||
debugConfig.noDebug = noDebug;
|
||||
|
||||
return vscode.debug.startDebugging(workspaceFolder, debugConfig);
|
||||
}
|
||||
|
|
143
src/extension.ts
143
src/extension.ts
|
@ -2,12 +2,14 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation } from "vscode-extension-telemetry-wrapper";
|
||||
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation,
|
||||
instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
|
||||
import * as commands from "./commands";
|
||||
import { JavaDebugConfigurationProvider } from "./configurationProvider";
|
||||
import { HCR_EVENT, JAVA_LANGID, USER_NOTIFICATION_EVENT } from "./constants";
|
||||
import { initializeCodeLensProvider } from "./debugCodeLensProvider"
|
||||
import { initializeCodeLensProvider, startDebugging } from "./debugCodeLensProvider"
|
||||
import { handleHotCodeReplaceCustomEvent, initializeHotCodeReplace } from "./hotCodeReplace";
|
||||
import { IMainMethod, resolveMainMethod } from "./languageServerPlugin";
|
||||
import { logger, Type } from "./logger";
|
||||
import * as utility from "./utility";
|
||||
|
||||
|
@ -23,8 +25,31 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
|
|||
description: "activateExtension",
|
||||
});
|
||||
|
||||
registerDebugEventListener(context);
|
||||
context.subscriptions.push(logger);
|
||||
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));
|
||||
context.subscriptions.push(instrumentOperationAsVsCodeCommand("JavaDebug.SpecifyProgramArgs", async () => {
|
||||
return specifyProgramArguments(context);
|
||||
}));
|
||||
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.hotCodeReplace", applyHCR));
|
||||
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.runJavaFile", async (uri: vscode.Uri) => {
|
||||
await runJavaFile(uri, true);
|
||||
}));
|
||||
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.debug.debugJavaFile", async (uri: vscode.Uri) => {
|
||||
await runJavaFile(uri, false);
|
||||
}));
|
||||
initializeHotCodeReplace(context);
|
||||
initializeCodeLensProvider(context);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export async function deactivate() {
|
||||
await disposeTelemetryWrapper();
|
||||
}
|
||||
|
||||
function registerDebugEventListener(context: vscode.ExtensionContext) {
|
||||
const measureKeys = ["duration"];
|
||||
vscode.debug.onDidTerminateDebugSession((e) => {
|
||||
context.subscriptions.push(vscode.debug.onDidTerminateDebugSession((e) => {
|
||||
if (e.type !== "java") {
|
||||
return;
|
||||
}
|
||||
|
@ -44,45 +69,8 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
|
|||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
context.subscriptions.push(logger);
|
||||
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));
|
||||
context.subscriptions.push(instrumentAndRegisterCommand("JavaDebug.SpecifyProgramArgs", async () => {
|
||||
return specifyProgramArguments(context);
|
||||
}));
|
||||
context.subscriptions.push(instrumentAndRegisterCommand("java.debug.hotCodeReplace", async (args: any) => {
|
||||
const autobuildConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("java.autobuild");
|
||||
if (!autobuildConfig.enabled) {
|
||||
const ans = await vscode.window.showWarningMessage(
|
||||
"The hot code replace feature requires you to enable the autobuild flag, do you want to enable it?",
|
||||
"Yes", "No");
|
||||
if (ans === "Yes") {
|
||||
await autobuildConfig.update("enabled", true);
|
||||
// Force an incremental build to avoid auto build is not finishing during HCR.
|
||||
try {
|
||||
await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false)
|
||||
} catch (err) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const debugSession: vscode.DebugSession = vscode.debug.activeDebugSession;
|
||||
if (!debugSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
return vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, async (progress) => {
|
||||
progress.report({ message: "Applying code changes..." });
|
||||
|
||||
const response = await debugSession.customRequest("redefineClasses");
|
||||
if (!response || !response.changedClasses || !response.changedClasses.length) {
|
||||
vscode.window.showWarningMessage("Cannot find any changed classes for hot replace!");
|
||||
}
|
||||
});
|
||||
}));
|
||||
initializeHotCodeReplace(context);
|
||||
context.subscriptions.push(vscode.debug.onDidReceiveDebugSessionCustomEvent((customEvent) => {
|
||||
const t = customEvent.session ? customEvent.session.type : undefined;
|
||||
if (t !== JAVA_LANGID) {
|
||||
|
@ -94,13 +82,6 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte
|
|||
handleUserNotification(customEvent);
|
||||
}
|
||||
}));
|
||||
|
||||
initializeCodeLensProvider(context);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export async function deactivate() {
|
||||
await disposeTelemetryWrapper();
|
||||
}
|
||||
|
||||
function handleUserNotification(customEvent) {
|
||||
|
@ -144,7 +125,69 @@ function specifyProgramArguments(context: vscode.ExtensionContext): Thenable<str
|
|||
});
|
||||
}
|
||||
|
||||
function instrumentAndRegisterCommand(name: string, cb: (...args: any[]) => any) {
|
||||
const instrumented = instrumentOperation(name, async (_operationId, myargs) => await cb(myargs));
|
||||
return vscode.commands.registerCommand(name, instrumented);
|
||||
async function applyHCR() {
|
||||
const autobuildConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("java.autobuild");
|
||||
if (!autobuildConfig.enabled) {
|
||||
const ans = await vscode.window.showWarningMessage(
|
||||
"The hot code replace feature requires you to enable the autobuild flag, do you want to enable it?",
|
||||
"Yes", "No");
|
||||
if (ans === "Yes") {
|
||||
await autobuildConfig.update("enabled", true);
|
||||
// Force an incremental build to avoid auto build is not finishing during HCR.
|
||||
try {
|
||||
await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false)
|
||||
} catch (err) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const debugSession: vscode.DebugSession = vscode.debug.activeDebugSession;
|
||||
if (!debugSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
return vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, async (progress) => {
|
||||
progress.report({ message: "Applying code changes..." });
|
||||
|
||||
const response = await debugSession.customRequest("redefineClasses");
|
||||
if (!response || !response.changedClasses || !response.changedClasses.length) {
|
||||
vscode.window.showWarningMessage("Cannot find any changed classes for hot replace!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function runJavaFile(uri: vscode.Uri, noDebug: boolean) {
|
||||
try {
|
||||
// Wait for Java Language Support extension being activated.
|
||||
await utility.getJavaExtensionAPI();
|
||||
} catch (ex) {
|
||||
if (ex instanceof utility.JavaExtensionNotActivatedError) {
|
||||
utility.guideToInstallJavaExtension();
|
||||
return;
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
|
||||
const mainMethods: IMainMethod[] = await resolveMainMethod(uri);
|
||||
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 projectName = mainMethods[0].projectName;
|
||||
let mainClass = mainMethods[0].mainClass;
|
||||
if (mainMethods.length > 1) {
|
||||
mainClass = await vscode.window.showQuickPick(mainMethods.map((mainMethod) => mainMethod.mainClass), {
|
||||
placeHolder: "Select the main class to launch.",
|
||||
});
|
||||
}
|
||||
|
||||
if (!mainClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
await startDebugging(mainClass, projectName, uri, noDebug);
|
||||
}
|
||||
|
|
|
@ -29,12 +29,7 @@ export interface ILaunchValidationResponse {
|
|||
}
|
||||
|
||||
export async function resolveMainMethod(uri: vscode.Uri): Promise<IMainMethod[]> {
|
||||
try {
|
||||
return <IMainMethod[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString());
|
||||
} catch (ex) {
|
||||
logger.log(Type.EXCEPTION, utility.formatErrorProperties(ex));
|
||||
return [];
|
||||
}
|
||||
return <IMainMethod[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString());
|
||||
}
|
||||
|
||||
export function startDebugSession() {
|
||||
|
|
|
@ -137,22 +137,24 @@ export function formatErrorProperties(ex: any): IProperties {
|
|||
}
|
||||
|
||||
export async function getJavaHome(): Promise<string> {
|
||||
const extension = vscode.extensions.getExtension(JAVA_EXTENSION_ID);
|
||||
if (!extension) {
|
||||
throw new JavaExtensionNotActivatedError("VS Code Java Extension is not enabled.");
|
||||
}
|
||||
try {
|
||||
const extensionApi = await extension.activate();
|
||||
if (extensionApi && extensionApi.javaRequirement) {
|
||||
return extensionApi.javaRequirement.java_home;
|
||||
}
|
||||
} catch (ex) {
|
||||
const extensionApi = await getJavaExtensionAPI();
|
||||
if (extensionApi && extensionApi.javaRequirement) {
|
||||
return extensionApi.javaRequirement.java_home;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
export function isJavaExtEnabled() {
|
||||
export function getJavaExtensionAPI(): Thenable<any> {
|
||||
const extension = vscode.extensions.getExtension(JAVA_EXTENSION_ID);
|
||||
if (!extension) {
|
||||
throw new JavaExtensionNotActivatedError("VS Code Java Extension is not enabled.");
|
||||
}
|
||||
|
||||
return extension.activate();
|
||||
}
|
||||
|
||||
export function isJavaExtEnabled(): boolean {
|
||||
const javaExt = vscode.extensions.getExtension(JAVA_EXTENSION_ID);
|
||||
return !!javaExt;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue