diff --git a/package.json b/package.json index 3ecdb7b..b2aea39 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,14 @@ "command": "cdt.debug.setOutputRadixToDecimal", "category": "CDT-GDB", "title": "Set Global Output Radix to Decimal" + }, + { + "title": "CDT-GDB: Set Hardware Breakpoint", + "command": "cdt.debug.breakpoints.addHardwareBreakpoint" + }, + { + "title": "CDT-GDB: Set Software Breakpoint", + "command": "cdt.debug.breakpoints.addSoftwareBreakpoint" } ], "breakpoints": [ @@ -885,6 +893,24 @@ { "command": "cdt.debug.setOutputRadixToDecimal", "when": "inDebugMode" + }, + { + "command": "cdt.debug.breakpoints.addHardwareBreakpoint", + "when": "false" + }, + { + "command": "cdt.debug.breakpoints.addSoftwareBreakpoint", + "when": "false" + } + ], + "editor/lineNumber/context": [ + { + "command": "cdt.debug.breakpoints.addHardwareBreakpoint", + "group": "navigation" + }, + { + "command": "cdt.debug.breakpoints.addSoftwareBreakpoint", + "group": "navigation" } ] } diff --git a/src/BreakpointModesController.ts b/src/BreakpointModesController.ts new file mode 100644 index 0000000..20a4b6c --- /dev/null +++ b/src/BreakpointModesController.ts @@ -0,0 +1,78 @@ +/********************************************************************* + * Copyright (c) 2026 Renesas Electronics Corporation and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *********************************************************************/ +import { + ExtensionContext, + Position, + Location, + SourceBreakpoint, + commands, + debug, + window, + Uri, +} from 'vscode'; +import { arePathsEqual } from './utils'; + +export class BreakpointModesController { + constructor(private context: ExtensionContext) {} + + setBreakpointHandler = + (mode: 'hardware' | 'software') => + async (values: { uri: Uri; lineNumber: number }) => { + try { + const line = values.lineNumber - 1; + const existingBreakpoints = debug.breakpoints.filter( + (bp: Partial) => + arePathsEqual( + bp?.location?.uri?.path, + values.uri.path, + ) && bp?.location?.range?.start?.line === line, + ); + if (existingBreakpoints.length) { + const existingBreakpointHasDesiredMode = + existingBreakpoints.findIndex( + (bp: any) => bp.mode === mode, + ) > -1; + if (existingBreakpointHasDesiredMode) { + return; + } + debug.removeBreakpoints(existingBreakpoints); + } + const sp = new SourceBreakpoint( + new Location(values.uri, new Position(line, 0)), + ); + // Limitation of VSCode: https://github.com/microsoft/vscode/issues/304764 + // This is a workaround to inject 'mode' into VS breakpoint object. + // This injection functionally working as expected and passes the information + // correctly through DAP messages, however the breakpoint mode information is not + // visible in the breakpoint list window in VSCode + (sp as any).mode = mode; + debug.addBreakpoints([sp]); + } catch (e) { + window.showErrorMessage( + `Failed to set ${mode} breakpoint: ${e}`, + ); + } + }; + + registerCommands = () => { + this.context.subscriptions.push( + commands.registerCommand( + 'cdt.debug.breakpoints.addHardwareBreakpoint', + this.setBreakpointHandler('hardware'), + ), + ); + this.context.subscriptions.push( + commands.registerCommand( + 'cdt.debug.breakpoints.addSoftwareBreakpoint', + this.setBreakpointHandler('software'), + ), + ); + }; +} diff --git a/src/extension.ts b/src/extension.ts index 58a3ee4..1b5e6cc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,6 +18,7 @@ import { CustomReset } from './CustomReset'; export { CustomReset } from './CustomReset'; import { SwitchRadix } from './switchRadix'; export { SwitchRadix } from './switchRadix'; +import { BreakpointModesController } from './BreakpointModesController'; export function activate(context: ExtensionContext) { new MemoryServer(context); @@ -40,6 +41,8 @@ export function activate(context: ExtensionContext) { }); }), ); + const breakpointModesController = new BreakpointModesController(context); + breakpointModesController.registerCommands(); } export function deactivate() { diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..9aa228d --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,41 @@ +/********************************************************************* + * Copyright (c) 2026 Renesas Electronics Corporation and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *********************************************************************/ +import { platform } from 'node:os'; +import { normalize } from 'node:path'; + +/** + * This function compares the given paths, returns true if they are equal. + * + * - The compare function normalises the paths before checking, which means '.' and '..' expressions + * reduced properly. + * - For Windows, the compare function is case insensitive + * - For Windows, the compare function also handles backslashes, forward slashes insensitively. + * - Compare function handles undefined values, which means given paths can be undefined. No need to check + * undefined before sending the arguments. + * + * @param path1 + * First path to compare + * @param path2 + * Second path to compare + */ +export const arePathsEqual = ( + path1: string | undefined, + path2: string | undefined, +): boolean => { + if (!path1 || !path2) { + return path1 === path2; + } + const nPath1 = normalize(path1); + const nPath2 = normalize(path2); + return platform() === 'win32' + ? nPath1.localeCompare(nPath2, undefined, { sensitivity: 'accent' }) === + 0 + : nPath1 === nPath2; +};