diff --git a/package.json b/package.json index c00f979f..b4eb630b 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,18 @@ } ] }, + "configuration": { + "properties": { + "debugpy.debugJustMyCode": { + "default": true, + "description": "%debugpy.debugJustMyCode%", + "scope": "resource", + "type": "boolean" + } + }, + "title": "Python Debugger", + "type": "object" + }, "debuggers": [ { "configurationAttributes": { diff --git a/package.nls.json b/package.nls.json index 6e2822cd..0e861715 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,5 +1,6 @@ { "debugpy.command.debugInTerminal.title": "Debug Python File", "debugpy.command.clearCacheAndReload.title": "Clear Cache and Reload Window", - "debugpy.command.viewOutput.title": "Show Output" + "debugpy.command.viewOutput.title": "Show Output", + "debugpy.debugJustMyCode": "When debugging only step through user-written code. Disable this to allow stepping into library code." } diff --git a/src/extension/debugger/configuration/resolvers/attach.ts b/src/extension/debugger/configuration/resolvers/attach.ts index f22aa77d..115e4627 100644 --- a/src/extension/debugger/configuration/resolvers/attach.ts +++ b/src/extension/debugger/configuration/resolvers/attach.ts @@ -7,6 +7,7 @@ import { CancellationToken, Uri, WorkspaceFolder } from 'vscode'; import { getOSType, OSType } from '../../../common/platform'; import { AttachRequestArguments, DebugOptions, PathMapping } from '../../../types'; import { BaseConfigurationResolver } from './base'; +import { getConfiguration } from '../../../common/vscodeapi'; export class AttachConfigurationResolver extends BaseConfigurationResolver { public async resolveDebugConfigurationWithSubstitutedVariables( @@ -42,16 +43,12 @@ export class AttachConfigurationResolver extends BaseConfigurationResolver('debugJustMyCode', true); } debugConfiguration.showReturnValue = debugConfiguration.showReturnValue !== false; // Pass workspace folder so we can get this when we get debug events firing. debugConfiguration.workspaceFolder = workspaceFolder ? workspaceFolder.fsPath : undefined; const debugOptions = debugConfiguration.debugOptions!; - if (!debugConfiguration.justMyCode) { - AttachConfigurationResolver.debugOption(debugOptions, DebugOptions.DebugStdLib); - } if (debugConfiguration.django) { AttachConfigurationResolver.debugOption(debugOptions, DebugOptions.Django); } diff --git a/src/extension/debugger/configuration/resolvers/launch.ts b/src/extension/debugger/configuration/resolvers/launch.ts index d42aa878..d2521087 100644 --- a/src/extension/debugger/configuration/resolvers/launch.ts +++ b/src/extension/debugger/configuration/resolvers/launch.ts @@ -11,6 +11,7 @@ import { DebugOptions, DebugPurpose, LaunchRequestArguments } from '../../../typ import { resolveVariables } from '../utils/common'; import { BaseConfigurationResolver } from './base'; import { getDebugEnvironmentVariables, getProgram } from './helper'; +import { getConfiguration } from '../../../common/vscodeapi'; export class LaunchConfigurationResolver extends BaseConfigurationResolver { public async resolveDebugConfiguration( @@ -102,15 +103,11 @@ export class LaunchConfigurationResolver extends BaseConfigurationResolver('debugJustMyCode', true); } // Pass workspace folder so we can get this when we get debug events firing. debugConfiguration.workspaceFolder = workspaceFolder ? workspaceFolder.fsPath : undefined; const debugOptions = debugConfiguration.debugOptions!; - if (!debugConfiguration.justMyCode) { - LaunchConfigurationResolver.debugOption(debugOptions, DebugOptions.DebugStdLib); - } if (debugConfiguration.stopOnEntry) { LaunchConfigurationResolver.debugOption(debugOptions, DebugOptions.StopOnEntry); } diff --git a/src/test/unittest/adapter/factory.unit.test.ts b/src/test/unittest/adapter/factory.unit.test.ts index 04fef451..5cd492c3 100644 --- a/src/test/unittest/adapter/factory.unit.test.ts +++ b/src/test/unittest/adapter/factory.unit.test.ts @@ -126,7 +126,7 @@ suite('Debugging - Adapter Factory', () => { assert.deepStrictEqual(descriptor, debugExecutable); }); - test.only('Display a message if no python interpreter is set', async () => { + test('Display a message if no python interpreter is set', async () => { getActiveEnvironmentPathStub.resolves(undefined); const session = createSession({}); const promise = factory.createDebugAdapterDescriptor(session, nodeExecutable); diff --git a/src/test/unittest/configuration/resolvers/attach.unit.test.ts b/src/test/unittest/configuration/resolvers/attach.unit.test.ts index 502d6970..64de3555 100644 --- a/src/test/unittest/configuration/resolvers/attach.unit.test.ts +++ b/src/test/unittest/configuration/resolvers/attach.unit.test.ts @@ -6,7 +6,15 @@ import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; import * as sinon from 'sinon'; -import { DebugConfiguration, DebugConfigurationProvider, TextDocument, TextEditor, Uri, WorkspaceFolder } from 'vscode'; +import { + DebugConfiguration, + DebugConfigurationProvider, + TextDocument, + TextEditor, + Uri, + WorkspaceConfiguration, + WorkspaceFolder, +} from 'vscode'; import { PYTHON_LANGUAGE } from '../../../../extension/common/constants'; import { getInfoPerOS } from './common'; import { AttachRequestArguments, DebugOptions } from '../../../../extension/types'; @@ -34,6 +42,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { let getActiveTextEditorStub: sinon.SinonStub; let getWorkspaceFoldersStub: sinon.SinonStub; let getOSTypeStub: sinon.SinonStub; + let getConfigurationStub: sinon.SinonStub; const debugOptionsAvailable = getAvailableOptions(); setup(() => { @@ -42,6 +51,8 @@ getInfoPerOS().forEach(([osName, osType, path]) => { getOSTypeStub = sinon.stub(platform, 'getOSType'); getWorkspaceFoldersStub = sinon.stub(vscodeapi, 'getWorkspaceFolders'); getOSTypeStub.returns(osType); + getConfigurationStub = sinon.stub(vscodeapi, 'getConfiguration'); + getConfigurationStub.withArgs('debugpy').returns(createMoqConfiguration(true)); }); teardown(() => { @@ -54,6 +65,14 @@ getInfoPerOS().forEach(([osName, osType, path]) => { return folder.object; } + function createMoqConfiguration(justMyCode: boolean) { + const debugpySettings = TypeMoq.Mock.ofType(); + debugpySettings + .setup((p) => p.get('debugJustMyCode', TypeMoq.It.isAny())) + .returns(() => justMyCode); + return debugpySettings.object; + } + function setupActiveEditor(fileName: string | undefined, languageId: string) { if (fileName) { const textEditor = TypeMoq.Mock.ofType(); @@ -493,67 +512,52 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const testsForJustMyCode = [ { justMyCode: false, - debugStdLib: true, + justMyCodeSetting: true, expectedResult: false, }, { justMyCode: false, - debugStdLib: false, + justMyCodeSetting: false, expectedResult: false, }, - { - justMyCode: false, - debugStdLib: undefined, - expectedResult: false, - }, - { - justMyCode: true, - debugStdLib: false, - expectedResult: true, - }, { justMyCode: true, - debugStdLib: true, + justMyCodeSetting: false, expectedResult: true, }, { justMyCode: true, - debugStdLib: undefined, - expectedResult: true, - }, - { - justMyCode: undefined, - debugStdLib: false, + justMyCodeSetting: true, expectedResult: true, }, { justMyCode: undefined, - debugStdLib: true, + justMyCodeSetting: false, expectedResult: false, }, { justMyCode: undefined, - debugStdLib: undefined, + justMyCodeSetting: true, expectedResult: true, }, ]; - test('Ensure justMyCode property is correctly derived from debugStdLib', async () => { - const activeFile = 'xyz.py'; - const workspaceFolder = createMoqWorkspaceFolder(__dirname); - setupActiveEditor(activeFile, PYTHON_LANGUAGE); - const defaultWorkspace = path.join('usr', 'desktop'); - setupWorkspaces([defaultWorkspace]); + testsForJustMyCode.forEach(async (testParams) => { + test('Ensure justMyCode property is correctly derived from global settings', async () => { + const activeFile = 'xyz.py'; + const workspaceFolder = createMoqWorkspaceFolder(__dirname); + setupActiveEditor(activeFile, PYTHON_LANGUAGE); + const defaultWorkspace = path.join('usr', 'desktop'); + setupWorkspaces([defaultWorkspace]); - const debugOptions = debugOptionsAvailable - .slice() - .concat(DebugOptions.Jinja, DebugOptions.Sudo) as DebugOptions[]; + const debugOptions = debugOptionsAvailable + .slice() + .concat(DebugOptions.Jinja, DebugOptions.Sudo) as DebugOptions[]; - testsForJustMyCode.forEach(async (testParams) => { + getConfigurationStub.withArgs('debugpy').returns(createMoqConfiguration(testParams.justMyCodeSetting)); const debugConfig = await resolveDebugConfiguration(workspaceFolder, { ...attach, debugOptions, justMyCode: testParams.justMyCode, - debugStdLib: testParams.debugStdLib, }); expect(debugConfig).to.have.property('justMyCode', testParams.expectedResult); }); diff --git a/src/test/unittest/configuration/resolvers/launch.unit.test.ts b/src/test/unittest/configuration/resolvers/launch.unit.test.ts index 6da329c6..4f52ba1e 100644 --- a/src/test/unittest/configuration/resolvers/launch.unit.test.ts +++ b/src/test/unittest/configuration/resolvers/launch.unit.test.ts @@ -6,7 +6,15 @@ import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; import * as sinon from 'sinon'; -import { DebugConfiguration, DebugConfigurationProvider, TextDocument, TextEditor, Uri, WorkspaceFolder } from 'vscode'; +import { + DebugConfiguration, + DebugConfigurationProvider, + TextDocument, + TextEditor, + Uri, + WorkspaceConfiguration, + WorkspaceFolder, +} from 'vscode'; import { PYTHON_LANGUAGE } from '../../../../extension/common/constants'; import { LaunchConfigurationResolver } from '../../../../extension/debugger/configuration/resolvers/launch'; import { getInfoPerOS } from './common'; @@ -31,6 +39,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { let getInterpreterDetailsStub: sinon.SinonStub; let getEnvFileStub: sinon.SinonStub; let getDebugEnvironmentVariablesStub: sinon.SinonStub; + let getConfigurationStub: sinon.SinonStub; setup(() => { getActiveTextEditorStub = sinon.stub(vscodeapi, 'getActiveTextEditor'); @@ -40,6 +49,8 @@ getInfoPerOS().forEach(([osName, osType, path]) => { getInterpreterDetailsStub = sinon.stub(pythonApi, 'getInterpreterDetails'); getEnvFileStub = sinon.stub(settings, 'getEnvFile'); getDebugEnvironmentVariablesStub = sinon.stub(helper, 'getDebugEnvironmentVariables'); + getConfigurationStub = sinon.stub(vscodeapi, 'getConfiguration'); + getConfigurationStub.withArgs('debugpy').returns(createMoqConfiguration(true)); }); teardown(() => { @@ -52,6 +63,14 @@ getInfoPerOS().forEach(([osName, osType, path]) => { return folder.object; } + function createMoqConfiguration(justMyCode: boolean) { + const debugpySettings = TypeMoq.Mock.ofType(); + debugpySettings + .setup((p) => p.get('debugJustMyCode', TypeMoq.It.isAny())) + .returns(() => justMyCode); + return debugpySettings.object; + } + function getClientOS() { return osType === platform.OSType.Windows ? 'windows' : 'unix'; } @@ -726,11 +745,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(debugConfig).to.have.property('redirectOutput', true); expect(debugConfig).to.have.property('justMyCode', false); expect(debugConfig).to.have.property('debugOptions'); - const expectedOptions = [ - DebugOptions.DebugStdLib, - DebugOptions.ShowReturnValue, - DebugOptions.RedirectOutput, - ]; + const expectedOptions = [DebugOptions.ShowReturnValue, DebugOptions.RedirectOutput]; if (osType === platform.OSType.Windows) { expectedOptions.push(DebugOptions.FixFilePathCase); } @@ -740,60 +755,45 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const testsForJustMyCode = [ { justMyCode: false, - debugStdLib: true, - expectedResult: false, - }, - { - justMyCode: false, - debugStdLib: false, + justMyCodeSetting: true, expectedResult: false, }, { justMyCode: false, - debugStdLib: undefined, + justMyCodeSetting: false, expectedResult: false, }, { justMyCode: true, - debugStdLib: false, - expectedResult: true, - }, - { - justMyCode: true, - debugStdLib: true, + justMyCodeSetting: false, expectedResult: true, }, { justMyCode: true, - debugStdLib: undefined, + justMyCodeSetting: true, expectedResult: true, }, { justMyCode: undefined, - debugStdLib: false, - expectedResult: true, - }, - { - justMyCode: undefined, - debugStdLib: true, + justMyCodeSetting: false, expectedResult: false, }, { justMyCode: undefined, - debugStdLib: undefined, + justMyCodeSetting: true, expectedResult: true, }, ]; - test('Ensure justMyCode property is correctly derived from debugStdLib', async () => { - const pythonPath = `PythonPath_${new Date().toString()}`; - const workspaceFolder = createMoqWorkspaceFolder(__dirname); - const pythonFile = 'xyz.py'; - setupIoc(pythonPath); - setupActiveEditor(pythonFile, PYTHON_LANGUAGE); - testsForJustMyCode.forEach(async (testParams) => { + testsForJustMyCode.forEach(async (testParams) => { + test('Ensure justMyCode property is correctly derived from global settings', async () => { + const pythonPath = `PythonPath_${new Date().toString()}`; + const workspaceFolder = createMoqWorkspaceFolder(__dirname); + const pythonFile = 'xyz.py'; + setupIoc(pythonPath); + setupActiveEditor(pythonFile, PYTHON_LANGUAGE); + getConfigurationStub.withArgs('debugpy').returns(createMoqConfiguration(testParams.justMyCodeSetting)); const debugConfig = await resolveDebugConfiguration(workspaceFolder, { ...launch, - debugStdLib: testParams.debugStdLib, justMyCode: testParams.justMyCode, }); expect(debugConfig).to.have.property('justMyCode', testParams.expectedResult); @@ -930,6 +930,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { request: requestType, type: 'python', name: '', + justMyCode: false, ...settings, }; const workspaceFolder = createMoqWorkspaceFolder(__dirname); diff --git a/src/test/unittest/extensionInit.unit.test.ts b/src/test/unittest/extensionInit.unit.test.ts index f54df24b..62737df7 100644 --- a/src/test/unittest/extensionInit.unit.test.ts +++ b/src/test/unittest/extensionInit.unit.test.ts @@ -71,6 +71,7 @@ suite('Debugging - register Debugging', () => { sinon.assert.calledWithExactly(registerCommandStub, Commands.ClearStorage, sinon.match.any); expect(registerCommandStub.callCount).to.be.equal(5); }); + test('Activation will register the Debug adapter factories', async () => { registerDebugger(context.object);