diff --git a/package.json b/package.json index d5b58868..fec89603 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,10 @@ "command": "java.debug.pauseOthers", "title": "Pause Others" }, + { + "command": "java.debug.breakpoints.exceptionTypes", + "title": "Debug: Breakpoint Exception Types..." + }, { "command": "java.debug.variables.showHex", "title": "Show as Hex" diff --git a/src/breakpointCommands.ts b/src/breakpointCommands.ts new file mode 100644 index 00000000..8281dcee --- /dev/null +++ b/src/breakpointCommands.ts @@ -0,0 +1,122 @@ +import * as vscode from "vscode"; + +export function registerBreakpointCommands(context: vscode.ExtensionContext): void { + context.subscriptions.push(vscode.commands.registerCommand('java.debug.breakpoints.exceptionTypes', exceptionTypes)); +} + +async function exceptionTypes() { + const config = vscode.workspace.getConfiguration('java.debug.settings.exceptionBreakpoint'); + let currentTypes = config.get('exceptionTypes', []); + const addExceptionTypeItem: vscode.QuickPickItem = { + label: '$(add) Add Exception Types...', + alwaysShow: true, + }; + const removeExceptionTypeItem: any = (type: string): any => ({ + label: type, + buttons: [{ + iconPath: new vscode.ThemeIcon('close'), + tooltip: 'Remove this Exception Type' + }] + }); + + // Step 1: Show Breakpoint Exception Types + const pickStep = async (state: any) => { + return new Promise((resolve) => { + const items: vscode.QuickPickItem[] = [ + addExceptionTypeItem, + ...currentTypes.map(type => removeExceptionTypeItem(type)) + ]; + const quickPick = vscode.window.createQuickPick(); + quickPick.items = items; + quickPick.title = 'Breakpoint Exception Types'; + quickPick.canSelectMany = false; + quickPick.matchOnDescription = false; + quickPick.matchOnDetail = false; + + quickPick.onDidAccept(() => { + const selected = quickPick.selectedItems[0]; + if (selected.label.includes('Add Exception Types')) { + quickPick.hide(); + // go to next step + resolve(state); + } + }); + + quickPick.onDidTriggerItemButton(async (e) => { + const typeToRemove = e.item.label; + currentTypes = currentTypes.filter(type => type !== typeToRemove); + await config.update('exceptionTypes', currentTypes, vscode.ConfigurationTarget.Global); + quickPick.items = [ + addExceptionTypeItem, + ...currentTypes.map(type => removeExceptionTypeItem(type)) + ]; + }); + quickPick.onDidHide(() => { + quickPick.dispose(); + }); + quickPick.show(); + }); + }; + + // Step 2: Add Exception Type(s) + const inputStep = async (state: any) => { + return new Promise((resolve, reject) => { + const input = vscode.window.createInputBox(); + input.title = 'Add Breakpoint Exception Type(s)'; + input.placeholder = 'Enter exception type(s) (comma or space separated)'; + input.prompt = 'Input exception types'; + input.buttons = [vscode.QuickInputButtons.Back]; + input.onDidAccept(async () => { + const exceptionType = input.value; + if (exceptionType) { + const types = exceptionType.split(/[,\s]+/).map(type => type.trim()).filter(type => type.length > 0); + let updated = false; + for (const type of types) { + if (!currentTypes.includes(type)) { + currentTypes.push(type); + updated = true; + } + } + if (updated) { + await config.update('exceptionTypes', currentTypes, vscode.ConfigurationTarget.Global); + } + } + input.hide(); + // go back to pick step + resolve(state); + }); + input.onDidTriggerButton((btn) => { + if (btn === vscode.QuickInputButtons.Back) { + input.hide(); + reject({ stepBack: true }); + } + }); + input.onDidHide(() => { + input.dispose(); + }); + input.show(); + }); + }; + + while (true) { + await multiStepInput([pickStep, inputStep], {}); + } +} + +async function multiStepInput(steps: Array<(input: T) => Promise>, initial: T): Promise { + let state = initial; + let currentStep = 0; + while (currentStep < steps.length) { + try { + state = await steps[currentStep](state); + currentStep++; + } catch (err) { + if (err?.stepBack) { + if (currentStep > 0) currentStep--; + } else { + throw err; + } + } + } + return state; +} diff --git a/src/extension.ts b/src/extension.ts index 6d8c2623..e370e8b0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,6 +25,7 @@ import { progressProvider } from "./progressImpl"; import { JavaTerminalLinkProvder } from "./terminalLinkProvider"; import { initializeThreadOperations } from "./threadOperations"; import * as utility from "./utility"; +import { registerBreakpointCommands } from "./breakpointCommands"; import { registerVariableMenuCommands } from "./variableMenu"; import { promisify } from "util"; @@ -36,6 +37,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { function initializeExtension(_operationId: string, context: vscode.ExtensionContext): any { registerDebugEventListener(context); + registerBreakpointCommands(context); registerVariableMenuCommands(context); context.subscriptions.push(vscode.window.registerTerminalLinkProvider(new JavaTerminalLinkProvder())); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));