11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33'use strict' ;
4+ import { injectable } from 'inversify' ;
45import { DebugAdapterTracker , Event , EventEmitter } from 'vscode' ;
56import { DebugProtocol } from 'vscode-debugprotocol' ;
67
78import { IDebugLocation } from './types' ;
89
910// When a python debugging session is active keep track of the current debug location
11+ @injectable ( )
1012export class DebugLocationTracker implements DebugAdapterTracker {
11- private waitingForStackTrace : boolean = false ;
13+ protected topMostFrameId = 0 ;
14+ protected sequenceNumbersOfRequestsPendingResponses = new Set < number > ( ) ;
15+ private waitingForStackTrace = false ;
1216 private _debugLocation : IDebugLocation | undefined ;
1317 private debugLocationUpdatedEvent : EventEmitter < void > = new EventEmitter < void > ( ) ;
1418 private sessionEndedEmitter : EventEmitter < DebugLocationTracker > = new EventEmitter < DebugLocationTracker > ( ) ;
1519
16- constructor ( private _sessionId : string ) {
20+ constructor ( private _sessionId : string | undefined ) {
1721 this . DebugLocation = undefined ;
1822 }
1923
@@ -33,8 +37,22 @@ export class DebugLocationTracker implements DebugAdapterTracker {
3337 return this . _debugLocation ;
3438 }
3539
36- // tslint:disable-next-line:no-any
37- public onDidSendMessage ( message : DebugProtocol . ProtocolMessage ) {
40+ public onDidSendMessage ( message : DebugProtocol . Response ) {
41+ if ( this . isResponseForRequestToFetchAllFrames ( message ) ) {
42+ // This should be the top frame. We need to use this to compute the value of a variable
43+ const topMostFrame = message . body . stackFrames [ 0 ] ;
44+ this . topMostFrameId = topMostFrame ?. id ;
45+ this . sequenceNumbersOfRequestsPendingResponses . delete ( message . request_seq ) ;
46+ // If we are waiting for a stack trace, check our messages for one
47+ if ( this . waitingForStackTrace ) {
48+ this . DebugLocation = {
49+ lineNumber : topMostFrame ?. line ,
50+ fileName : this . normalizeFilePath ( topMostFrame ?. source ?. path ) ,
51+ column : topMostFrame . column
52+ } ;
53+ this . waitingForStackTrace = false ;
54+ }
55+ }
3856 if ( this . isStopEvent ( message ) ) {
3957 // Some type of stop, wait to see our next stack trace to find our location
4058 this . waitingForStackTrace = true ;
@@ -45,21 +63,23 @@ export class DebugLocationTracker implements DebugAdapterTracker {
4563 this . DebugLocation = undefined ;
4664 this . waitingForStackTrace = false ;
4765 }
48-
49- if ( this . waitingForStackTrace ) {
50- // If we are waiting for a stack track, check our messages for one
51- const debugLoc = this . getStackTrace ( message ) ;
52- if ( debugLoc ) {
53- this . DebugLocation = debugLoc ;
54- this . waitingForStackTrace = false ;
55- }
56- }
5766 }
5867
5968 public onWillStopSession ( ) {
6069 this . sessionEndedEmitter . fire ( this ) ;
6170 }
6271
72+ public onWillReceiveMessage ( message : DebugProtocol . Request ) {
73+ if ( this . isRequestToFetchAllFrames ( message ) ) {
74+ // VSCode sometimes sends multiple stackTrace requests. The true topmost frame is determined
75+ // based on the response to a stackTrace request where the startFrame is 0 or undefined (i.e.
76+ // this request retrieves all frames). Here, remember the sequence number of the outgoing
77+ // request whose startFrame === 0 or undefined, and update this.topMostFrameId only when we
78+ // receive the response with a matching sequence number.
79+ this . sequenceNumbersOfRequestsPendingResponses . add ( message . seq ) ;
80+ }
81+ }
82+
6383 // Set our new location and fire our debug event
6484 private set DebugLocation ( newLocation : IDebugLocation | undefined ) {
6585 const oldLocation = this . _debugLocation ;
@@ -70,7 +90,15 @@ export class DebugLocationTracker implements DebugAdapterTracker {
7090 }
7191 }
7292
73- // tslint:disable-next-line:no-any
93+ private normalizeFilePath ( path : string ) : string {
94+ // Make the path match the os. Debugger seems to return
95+ // invalid path chars on linux/darwin
96+ if ( process . platform !== 'win32' ) {
97+ return path . replace ( / \\ / g, '/' ) ;
98+ }
99+ return path ;
100+ }
101+
74102 private isStopEvent ( message : DebugProtocol . ProtocolMessage ) {
75103 if ( message . type === 'event' ) {
76104 const eventMessage = message as DebugProtocol . Event ;
@@ -82,34 +110,6 @@ export class DebugLocationTracker implements DebugAdapterTracker {
82110 return false ;
83111 }
84112
85- // tslint:disable-next-line:no-any
86- private getStackTrace ( message : DebugProtocol . ProtocolMessage ) : IDebugLocation | undefined {
87- if ( message . type === 'response' ) {
88- const responseMessage = message as DebugProtocol . Response ;
89- if ( responseMessage . command === 'stackTrace' ) {
90- const messageBody = responseMessage . body ;
91- if ( messageBody . stackFrames . length > 0 ) {
92- const lineNumber = messageBody . stackFrames [ 0 ] . line ;
93- const fileName = this . normalizeFilePath ( messageBody . stackFrames [ 0 ] . source . path ) ;
94- const column = messageBody . stackFrames [ 0 ] . column ;
95- return { lineNumber, fileName, column } ;
96- }
97- }
98- }
99-
100- return undefined ;
101- }
102-
103- private normalizeFilePath ( path : string ) : string {
104- // Make the path match the os. Debugger seems to return
105- // invalid path chars on linux/darwin
106- if ( process . platform !== 'win32' ) {
107- return path . replace ( / \\ / g, '/' ) ;
108- }
109- return path ;
110- }
111-
112- // tslint:disable-next-line:no-any
113113 private isContinueEvent ( message : DebugProtocol . ProtocolMessage ) : boolean {
114114 if ( message . type === 'event' ) {
115115 const eventMessage = message as DebugProtocol . Event ;
@@ -125,4 +125,21 @@ export class DebugLocationTracker implements DebugAdapterTracker {
125125
126126 return false ;
127127 }
128+
129+ private isResponseForRequestToFetchAllFrames ( message : DebugProtocol . Response ) {
130+ return (
131+ message . type === 'response' &&
132+ message . command === 'stackTrace' &&
133+ message . body . stackFrames [ 0 ] &&
134+ this . sequenceNumbersOfRequestsPendingResponses . has ( message . request_seq )
135+ ) ;
136+ }
137+
138+ private isRequestToFetchAllFrames ( message : DebugProtocol . Request ) {
139+ return (
140+ message . type === 'request' &&
141+ message . command === 'stackTrace' &&
142+ ( message . arguments . startFrame === 0 || message . arguments . startFrame === undefined )
143+ ) ;
144+ }
128145}
0 commit comments