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