Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1 Enhancements/6672.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add debug command code lenses when in debug mode
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,26 @@
"title": "%python.command.python.datascience.runcurrentcell.title%",
"category": "Python"
},
{
"command": "python.datascience.debugcell",
"title": "%python.command.python.datascience.debugcell.title%",
"category": "Python"
},
{
"command": "python.datascience.debugstepover",
"title": "%python.command.python.datascience.debugstepover.title%",
"category": "Python"
},
{
"command": "python.datascience.debugstop",
"title": "%python.command.python.datascience.debugstop.title%",
"category": "Python"
},
{
"command": "python.datascience.debugcontinue",
"title": "%python.command.python.datascience.debugcontinue.title%",
"category": "Python"
},
{
"command": "python.datascience.runcurrentcelladvance",
"title": "%python.command.python.datascience.runcurrentcelladvance.title%",
Expand Down Expand Up @@ -1430,6 +1450,12 @@
"description": "Set of commands to put as code lens above a cell. Defaults to 'python.datascience.runcell, python.datascience.runallcellsabove, python.datascience.debugcell'",
"scope": "resource"
},
"python.dataScience.debugCodeLenses": {
"type": "string",
"default": "python.datascience.debugcontinue, python.datascience.debugstop, python.datascience.debugstepover",
"description": "Set of debug commands to put as code lens above a cell while debugging.",
"scope": "resource"
},
"python.dataScience.ptvsdDistPath": {
"type": "string",
"default": "",
Expand Down
6 changes: 6 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
"python.command.python.datascience.runcurrentcellandallbelow.palette.title": "Run Current Cell and Below",
"python.command.python.datascience.debugcurrentcell.palette.title": "Debug Current Cell",
"python.command.python.datascience.debugcell.title": "Debug Cell",
"python.command.python.datascience.debugstepover.title": "Step Over",
"python.command.python.datascience.debugcontinue.title": "Continue",
"python.command.python.datascience.debugstop.title": "Stop",
"python.command.python.datascience.runtoline.title": "Run To Line in Python Interactive Window",
"python.command.python.datascience.runfromline.title": "Run From Line in Python Interactive Window",
"python.command.python.datascience.runcurrentcell.title": "Run Current Cell",
Expand Down Expand Up @@ -305,6 +308,9 @@
"DataScience.jupyterDataRateExceeded": "Cannot view variable because data rate exceeded. Please restart your server with a higher data rate limit. For example, --NotebookApp.iopub_data_rate_limit=10000000000.0",
"DataScience.addCellBelowCommandTitle": "Add cell",
"DataScience.debugCellCommandTitle": "Debug cell",
"DataScience.debugStepOverCommandTitle": "Step over",
"DataScience.debugContinueCommandTitle": "Continue",
"DataScience.debugStopCommandTitle": "Stop",
"DataScience.runCurrentCellAndAddBelow": "Run current and add cell below",
"DataScience.variableExplorerDisabledDuringDebugging": "Please see the Debug Side Bar's VARIABLES section.",
"DataScience.jupyterDebuggerNotInstalledError": "Pip module ptvsd is required for debugging cells. You will need to install it to debug cells.",
Expand Down
5 changes: 5 additions & 0 deletions src/client/common/application/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface ICommandNameWithoutArgumentTypeMapping {
[Commands.Set_ShebangInterpreter]: [];
[Commands.Run_Linter]: [];
[Commands.Enable_Linter]: [];
['workbench.action.debug.continue']: [];
['workbench.action.debug.stepOver']: [];
['workbench.action.debug.stop']: [];
['workbench.action.reloadWindow']: [];
['editor.action.formatDocument']: [];
Expand Down Expand Up @@ -115,6 +117,9 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
[DSCommands.RunFileInInteractiveWindows]: [string];
[DSCommands.DebugFileInInteractiveWindows]: [string];
[DSCommands.DebugCell]: [string, number, number, number, number];
[DSCommands.DebugStepOver]: [];
[DSCommands.DebugStop]: [];
[DSCommands.DebugContinue]: [];
[DSCommands.RunCurrentCellAndAddBelow]: [string];
[DSCommands.ScrollToCell]: [string, string];
}
4 changes: 4 additions & 0 deletions src/client/common/application/debugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export class DebugService implements IDebugService {
public registerDebugConfigurationProvider(debugType: string, provider: any): Disposable {
return debug.registerDebugConfigurationProvider(debugType, provider);
}
// tslint:disable-next-line:no-any
public registerDebugAdapterTrackerFactory(debugType: string, provider: any): Disposable {
return debug.registerDebugAdapterTrackerFactory(debugType, provider);
}
public startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSession?: DebugSession): Thenable<boolean> {
return debug.startDebugging(folder, nameOrConfiguration, parentSession);
}
Expand Down
10 changes: 10 additions & 0 deletions src/client/common/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CancellationToken,
CompletionItemProvider,
ConfigurationChangeEvent,
DebugAdapterTrackerFactory,
DebugConfiguration,
DebugConfigurationProvider,
DebugConsole,
Expand Down Expand Up @@ -764,6 +765,15 @@ export interface IDebugService {
*/
registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider): Disposable;

/**
* Register a debug adapter tracker factory for the given debug type.
*
* @param debugType The debug type for which the factory is registered or '*' for matching all debug types.
* @param factory The [debug adapter tracker factory](#DebugAdapterTrackerFactory) to register.
* @return A [disposable](#Disposable) that unregisters this factory when being disposed.
*/
registerDebugAdapterTrackerFactory(debugType: string, factory: DebugAdapterTrackerFactory): Disposable;

/**
* Start debugging by using either a named launch or named compound configuration,
* or by directly passing a [DebugConfiguration](#DebugConfiguration).
Expand Down
1 change: 1 addition & 0 deletions src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export interface IDataScienceSettings {
askForKernelRestart?: boolean;
enablePlotViewer?: boolean;
codeLenses?: string;
debugCodeLenses?: string;
ptvsdDistPath?: string;
stopOnFirstLineWhileDebugging?: boolean;
textOutputLimit?: number;
Expand Down
3 changes: 3 additions & 0 deletions src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ export namespace DataScience {
export const jupyterDataRateExceeded = localize('DataScience.jupyterDataRateExceeded', 'Cannot view variable because data rate exceeded. Please restart your server with a higher data rate limit. For example, --NotebookApp.iopub_data_rate_limit=10000000000.0');
export const addCellBelowCommandTitle = localize('DataScience.addCellBelowCommandTitle', 'Add cell');
export const debugCellCommandTitle = localize('DataScience.debugCellCommandTitle', 'Debug cell');
export const debugStepOverCommandTitle = localize('DataScience.debugStepOverCommandTitle', 'Step over');
export const debugContinueCommandTitle = localize('DataScience.debugContinueCommandTitle', 'Continue');
export const debugStopCommandTitle = localize('DataScience.debugStopCommandTitle', 'Stop');
export const runCurrentCellAndAddBelow = localize('DataScience.runCurrentCellAndAddBelow', 'Run current and add cell below');
export const variableExplorerDisabledDuringDebugging = localize('DataScience.variableExplorerDisabledDuringDebugging', 'Please see the Debug Side Bar\'s VARIABLES section.');
export const jupyterDebuggerNotInstalledError = localize('DataScience.jupyterDebuggerNotInstalledError', 'Pip module ptvsd is required for debugging cells. You will need to install it to debug cells.');
Expand Down
17 changes: 16 additions & 1 deletion src/client/datascience/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,22 @@ export namespace Commands {
export const AddCellBelow = 'python.datascience.addcellbelow';
export const DebugCurrentCellPalette = 'python.datascience.debugcurrentcell.palette';
export const DebugCell = 'python.datascience.debugcell';
export const DebugStepOver = 'python.datascience.debugstepover';
export const DebugContinue = 'python.datascience.debugcontinue';
export const DebugStop = 'python.datascience.debugstop';
export const RunCurrentCellAndAddBelow = 'python.datascience.runcurrentcellandaddbelow';
export const ScrollToCell = 'python.datascience.scrolltocell';
}

export namespace CodeLensCommands {
// If not specified in the options this is the default set of commands in our design time code lenses
export const DefaultDesignLenses = [Commands.RunCurrentCell, Commands.RunAllCellsAbove, Commands.DebugCell];
// If not specified in the options this is the default set of commands in our debug time code lenses
export const DefaultDebuggingLenses = [Commands.DebugContinue, Commands.DebugStop, Commands.DebugStepOver];
// These are the commands that are allowed at debug time
export const DebuggerCommands = [Commands.DebugContinue, Commands.DebugStop, Commands.DebugStepOver];
}

export namespace EditorContexts {
export const HasCodeCells = 'python.datascience.hascodecells';
export const DataScienceEnabled = 'python.datascience.featureenabled';
Expand Down Expand Up @@ -152,7 +164,10 @@ export enum Telemetry {
PtvsdPromptToInstall = 'DATASCIENCE.PTVSD_PROMPT_TO_INSTALL',
PtvsdSuccessfullyInstalled = 'DATASCIENCE.PTVSD_SUCCESSFULLY_INSTALLED',
PtvsdInstallFailed = 'DATASCIENCE.PTVSD_INSTALL_FAILED',
ScrolledToCell = 'DATASCIENCE.SCROLLED_TO_CELL'
ScrolledToCell = 'DATASCIENCE.SCROLLED_TO_CELL',
DebugStepOver = 'DATASCIENCE.DEBUG_STEP_OVER',
DebugContinue = 'DATASCIENCE.DEBUG_CONTINUE',
DebugStop = 'DATASCIENCE.DEBUG_STOP'
}

export namespace HelpLinks {
Expand Down
39 changes: 38 additions & 1 deletion src/client/datascience/datascience.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { inject, injectable } from 'inversify';
import { URL } from 'url';
import * as vscode from 'vscode';

import { IApplicationShell, ICommandManager, IDocumentManager, IWorkspaceService } from '../common/application/types';
import { IApplicationShell, ICommandManager, IDebugService, IDocumentManager, IWorkspaceService } from '../common/application/types';
import { PYTHON_ALLFILES, PYTHON_LANGUAGE } from '../common/constants';
import { ContextKey } from '../common/contextKey';
import { traceError } from '../common/logger';
Expand Down Expand Up @@ -43,6 +43,7 @@ export class DataScience implements IDataScience {
@inject(IConfigurationService) private configuration: IConfigurationService,
@inject(IDocumentManager) private documentManager: IDocumentManager,
@inject(IApplicationShell) private appShell: IApplicationShell,
@inject(IDebugService) private debugService: IDebugService,
@inject(IWorkspaceService) private workspace: IWorkspaceService
) {
this.commandListeners = this.serviceContainer.getAll<IDataScienceCommandListener>(IDataScienceCommandListener);
Expand Down Expand Up @@ -243,6 +244,36 @@ export class DataScience implements IDataScience {
}
}

@captureTelemetry(Telemetry.DebugStepOver)
public async debugStepOver(): Promise<void> {
this.dataScienceSurveyBanner.showBanner().ignoreErrors();

// Make sure that we are in debug mode
if (this.debugService.activeDebugSession) {
this.commandManager.executeCommand('workbench.action.debug.stepOver');
}
}

@captureTelemetry(Telemetry.DebugStop)
public async debugStop(): Promise<void> {
this.dataScienceSurveyBanner.showBanner().ignoreErrors();

// Make sure that we are in debug mode
if (this.debugService.activeDebugSession) {
this.commandManager.executeCommand('workbench.action.debug.stop');
}
}

@captureTelemetry(Telemetry.DebugContinue)
public async debugContinue(): Promise<void> {
this.dataScienceSurveyBanner.showBanner().ignoreErrors();

// Make sure that we are in debug mode
if (this.debugService.activeDebugSession) {
this.commandManager.executeCommand('workbench.action.debug.continue');
}
}

@captureTelemetry(Telemetry.SetJupyterURIToLocal)
private async setJupyterURIToLocal(): Promise<void> {
await this.configuration.updateSetting('dataScience.jupyterServerURI', Settings.JupyterServerLocalLaunch, undefined, vscode.ConfigurationTarget.Workspace);
Expand Down Expand Up @@ -417,6 +448,12 @@ export class DataScience implements IDataScience {
this.disposableRegistry.push(disposable);
disposable = this.commandManager.registerCommand(Commands.DebugCell, this.debugCell, this);
this.disposableRegistry.push(disposable);
disposable = this.commandManager.registerCommand(Commands.DebugStepOver, this.debugStepOver, this);
this.disposableRegistry.push(disposable);
disposable = this.commandManager.registerCommand(Commands.DebugContinue, this.debugContinue, this);
this.disposableRegistry.push(disposable);
disposable = this.commandManager.registerCommand(Commands.DebugStop, this.debugStop, this);
this.disposableRegistry.push(disposable);
disposable = this.commandManager.registerCommand(Commands.DebugCurrentCellPalette, this.debugCurrentCellFromCursor, this);
this.disposableRegistry.push(disposable);
this.commandListeners.forEach((listener: IDataScienceCommandListener) => {
Expand Down
110 changes: 110 additions & 0 deletions src/client/datascience/debugLocationTracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import { injectable } from 'inversify';
import { DebugSession, Event, EventEmitter } from 'vscode';
import { DebugProtocol } from 'vscode-debugprotocol';

import { IDebugLocation, IDebugLocationTracker } from './types';

// When a python debugging session is active keep track of the current debug location
@injectable()
export class DebugLocationTracker implements IDebugLocationTracker {
private waitingForStackTrace: boolean = false;
private _debugLocation: IDebugLocation | undefined;
private debugLocationUpdatedEvent: EventEmitter<void> = new EventEmitter<void>();

public setDebugSession(_targetSession: DebugSession) {
this.DebugLocation = undefined;
this.waitingForStackTrace = false;
}

public get debugLocationUpdated(): Event<void> {
return this.debugLocationUpdatedEvent.event;
}

public get debugLocation(): IDebugLocation | undefined {
return this._debugLocation;
}

// tslint:disable-next-line:no-any
public onDidSendMessage(message: DebugProtocol.ProtocolMessage) {
if (this.isStopEvent(message)) {
// Some type of stop, wait to see our next stack trace to find our location
this.waitingForStackTrace = true;
}

if (this.isContinueEvent(message)) {
// Running, clear the location
this.DebugLocation = undefined;
this.waitingForStackTrace = false;
}

if (this.waitingForStackTrace) {
// If we are waiting for a stack track, check our messages for one
const debugLoc = this.getStackTrace(message);
if (debugLoc) {
this.DebugLocation = debugLoc;
this.waitingForStackTrace = false;
}
}

}

// Set our new location and fire our debug event
private set DebugLocation(newLocation: IDebugLocation | undefined) {
const oldLocation = this._debugLocation;
this._debugLocation = newLocation;

if (this._debugLocation !== oldLocation) {
this.debugLocationUpdatedEvent.fire();
}
}

// tslint:disable-next-line:no-any
private isStopEvent(message: DebugProtocol.ProtocolMessage) {
if (message.type === 'event') {
const eventMessage = message as DebugProtocol.Event;
if (eventMessage.event === 'stopped') {
return true;
}
}

return false;
}

// tslint:disable-next-line:no-any
private getStackTrace(message: DebugProtocol.ProtocolMessage): IDebugLocation | undefined {
if (message.type === 'response') {
const responseMessage = message as DebugProtocol.Response;
if (responseMessage.command === 'stackTrace') {
const messageBody = responseMessage.body;
if (messageBody.stackFrames.length > 0) {
const lineNumber = messageBody.stackFrames[0].line;
const fileName = messageBody.stackFrames[0].source.path;
const column = messageBody.stackFrames[0].column;
return { lineNumber, fileName, column };
}
}
}

return undefined;
}

// tslint:disable-next-line:no-any
private isContinueEvent(message: DebugProtocol.ProtocolMessage): boolean {
if (message.type === 'event') {
const eventMessage = message as DebugProtocol.Event;
if (eventMessage.event === 'continue') {
return true;
}
} else if (message.type === 'response') {
const responseMessage = message as DebugProtocol.Response;
if (responseMessage.command === 'continue') {
return true;
}
}

return false;
}
}
26 changes: 26 additions & 0 deletions src/client/datascience/debugLocationTrackerFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import { inject, injectable } from 'inversify';
import { DebugAdapterTracker, DebugSession, ProviderResult } from 'vscode';

import { IDebugService } from '../common/application/types';
import { IDisposableRegistry } from '../common/types';
import { IDebugLocationTracker, IDebugLocationTrackerFactory } from './types';

// Hook up our IDebugLocationTracker to python debugging sessions
@injectable()
export class DebugLocationTrackerFactory implements IDebugLocationTrackerFactory {
constructor(
@inject(IDebugLocationTracker) private locationTracker: IDebugLocationTracker,
@inject(IDebugService) debugService: IDebugService,
@inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry
) {
disposableRegistry.push(debugService.registerDebugAdapterTrackerFactory('python', this));
}

public createDebugAdapterTracker(session: DebugSession): ProviderResult<DebugAdapterTracker> {
this.locationTracker.setDebugSession(session);
return this.locationTracker;
}
}
Loading