Visual Studio Code – Adding a Breakpoint Keybind Toggle

As I continued to look for new issues in an effort to contribute to Visual Studio Code, it was back to the 4000+ issue list for my next bug.
In no time I came across a recently filed “feature request”. #46509 – Disable breakpoint command.

If you’re not a regular user of Visual Studio Code, it’s important to point out that it comes bundled with great debugging support. VS Code’s built-in debugger helps accelerate your edit, compile and debug loop.

Along with the debugger tools, Visual Studio Code also provides a rich and easy keybinding editing experience. It lists all available commands with and without key bindings. Allowing you to easily change, remove and reset their key bindings using the available actions.

Keyboard Shortcuts

I believe it’s important for a developer to master their IDE environment and using key bindings is a great way to improve your productivity.

Issue#46509 ties both of these features together in order to deliver a more efficient experience

Since I’ve never worked with vs code’s key binding and debugging features before, some research was required.

VScode maintainer isidor, had suggested that this new command would live in /src/vs/workbench/parts/debug/browser/debugcommands.ts. This is where all the debugger commands for key bindings and workbench tools can be found.

I figured a great place to start would by analyzing how a similar feature has been implemented. The Debug already has the two functions which Enable all breakpoints and Disable all breakpoints.
I made a conscious effort to analyze and understand each line of the debug.toggleBreakpoint function to get a better idea of how to add the new command.


Lets break down the debug.toggleBreakpointcommand code block from /src/vs/workbench/parts/debug/browser/debugCommands.ts#L176-L203

Immediately we see that were defining an registerCommandAndKeybindingRule object defined by an IKeybindingsRegistry interface.

In order to define a key binding command we need to provide the following parameters:

  • id: how the command is identified in the key bindings pages
  • weight: will this command be apart of vs code’s code, workbench, or a builtin/external extension
  • when: ensures that the command can only be executed during the defined state
  • primary: the default key bindings

		id: 'debug.toggleBreakpoint',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5),
		when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),
		primary: KeyCode.Space,

Next, we have the handler, which like the name suggests, handles the command functionality.
Before executing a command we need to get the current state of the editor’s environment. Essentially we’re checking to see if the user is currently focused in the editor window.
If we are able to get control of the editor, we can access the objects within it. In this case getFocusedElements() function will return a list of all breakpoints within the current window.

		handler: (accessor) => {
			const listService = accessor.get(IListService);
			const debugService = accessor.get(IDebugService);
			const list = listService.lastFocusedList;
			if (list instanceof List) {
				const focused = <IEnablement[]>list.getFocusedElements();
				if (focused && focused.length) {
					debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]).done(null, errors.onUnexpectedError);

If the window is focused, enableOrDisableBreakpoints function will enable or disable ALL breakpoints.
Key word being all.

I’ve found that breaking down pieces of code line by line has helped big time in reading new code.


Following our previous analysis, I began to plugin the KeyBindingRegistry required fields. (id, weight, primary, when).
Similar to the debug.toggleBreakpoint function we need to get the current state of the editor. Along with creating a debugService handler.

The handler from the analysis was looping through a List of breakpoints. Since we want to toggle a single breakpoint, we will not be requiring this service. Instead, we need a to retrieve the underlying control of the editor by creating a variable containing an ICodeEditor object.

const control = <ICodeEditor>editor.getControl();

Once we verify that our control variable is true, we begin to retrieve the status of our debug services.

				const position = control.getPosition(); // retrieves the active cursor position
				const modelUri = control.getModel().uri; // a uri model

Since we only need the status of an individual breakpoint we need to filter the debugService by column and line number. At the end, we call the pop() function to return the last element of the array and store it a const bp variable.

	const bp = debugService.getModel().getBreakpoints()
					.filter(bp => bp.lineNumber === position.lineNumber, bp => bp.column === position.column && bp.uri.toString() === modelUri.toString()).pop();

Once we verify that bp is a valid IBreakpoint object, we can begin to toggle it’s status.
The following function checks if the breakpoint is enabled or disabled and returns the opposite value.

debugService.enableOrDisableBreakpoints(!bp.enabled, bp).done(null, errors.onUnexpectedError);

Once we build and run our code, we see that our newly added command has been added to the key binding list where we can assign a shortcut.


Related Posts