From 85094230a721df8dea1b685dd4d3a845d16d8eb4 Mon Sep 17 00:00:00 2001 From: shreeraj Date: Mon, 2 Nov 2026 16:10:22 +0530 Subject: [PATCH] Add stopAllThreadsOnSuspend and steppingResumesAllThreads to the launch.json schema debugpy already reads both options for launch and attach requests, but they were missing from the debugpy configuration attributes, so VS Code reported them as unknown properties in launch.json and offered no description or completion for them. --- package.json | 20 +++++++++++++ src/extension/types.ts | 4 +++ .../unittest/launchJsonSchema.unit.test.ts | 29 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/test/unittest/launchJsonSchema.unit.test.ts diff --git a/package.json b/package.json index 2f44247f..80f39d57 100644 --- a/package.json +++ b/package.json @@ -355,6 +355,16 @@ "description": "Show return value of functions when stepping.", "type": "boolean" }, + "steppingResumesAllThreads": { + "default": true, + "description": "If true, all threads are resumed while stepping. If false, only the thread being stepped is resumed.", + "type": "boolean" + }, + "stopAllThreadsOnSuspend": { + "default": true, + "description": "If true, all threads are suspended when a breakpoint is hit or the program is paused. If false, only the thread that triggered the stop is suspended.", + "type": "boolean" + }, "subProcess": { "default": false, "description": "Whether to enable Sub Process debugging", @@ -633,6 +643,16 @@ "description": "Show return value of functions when stepping.", "type": "boolean" }, + "steppingResumesAllThreads": { + "default": true, + "description": "If true, all threads are resumed while stepping. If false, only the thread being stepped is resumed.", + "type": "boolean" + }, + "stopAllThreadsOnSuspend": { + "default": true, + "description": "If true, all threads are suspended when a breakpoint is hit or the program is paused. If false, only the thread that triggered the stop is suspended.", + "type": "boolean" + }, "stopOnEntry": { "default": false, "description": "Automatically stop after launch.", diff --git a/src/extension/types.ts b/src/extension/types.ts index 37dcb738..50c86339 100644 --- a/src/extension/types.ts +++ b/src/extension/types.ts @@ -73,6 +73,10 @@ interface ICommonDebugArguments { host?: string; // Show return values of functions while stepping. showReturnValue?: boolean; + // Resume all threads (true, the default) or only the stepping thread (false) during a step. + steppingResumesAllThreads?: boolean; + // Suspend all threads (true, the default) or only the stopping thread (false) on a breakpoint or pause. + stopAllThreadsOnSuspend?: boolean; subProcess?: boolean; // SystemExit codes (and/or inclusive ranges) that cause the debugger to break. // An empty array disables breaking on SystemExit entirely. diff --git a/src/test/unittest/launchJsonSchema.unit.test.ts b/src/test/unittest/launchJsonSchema.unit.test.ts new file mode 100644 index 00000000..91fde9d2 --- /dev/null +++ b/src/test/unittest/launchJsonSchema.unit.test.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import * as fs from 'fs'; +import * as path from 'path'; + +suite('Debugging - launch.json schema', () => { + const packageJsonPath = path.resolve(__dirname, '..', '..', '..', 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const debuggerContribution = packageJson.contributes.debuggers.find((d: { type: string }) => d.type === 'debugpy'); + + ['launch', 'attach'].forEach((request) => { + suite(`${request} configuration`, () => { + const properties = debuggerContribution.configurationAttributes[request].properties; + + ['steppingResumesAllThreads', 'stopAllThreadsOnSuspend'].forEach((option) => { + test(`${option} is declared as a boolean that defaults to true`, () => { + expect(properties).to.have.property(option); + expect(properties[option].type).to.equal('boolean'); + expect(properties[option].default).to.equal(true); + expect(properties[option].description).to.be.a('string').that.is.not.empty; + }); + }); + }); + }); +});