roku-debug 0.21.15 → 0.21.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/LaunchConfiguration.d.ts +22 -0
  3. package/dist/adapters/DebugProtocolAdapter.d.ts +12 -4
  4. package/dist/adapters/DebugProtocolAdapter.js +47 -6
  5. package/dist/adapters/DebugProtocolAdapter.js.map +1 -1
  6. package/dist/adapters/TelnetAdapter.d.ts +2 -13
  7. package/dist/adapters/TelnetAdapter.js +13 -10
  8. package/dist/adapters/TelnetAdapter.js.map +1 -1
  9. package/dist/adapters/customVariableUtils.d.ts +4 -0
  10. package/dist/adapters/customVariableUtils.js +398 -26
  11. package/dist/adapters/customVariableUtils.js.map +1 -1
  12. package/dist/adapters/customVariables/ifAppInfo.d.ts +2 -0
  13. package/dist/adapters/customVariables/ifAppInfo.js +50 -0
  14. package/dist/adapters/customVariables/ifAppInfo.js.map +1 -0
  15. package/dist/adapters/customVariables/ifAppManager.d.ts +2 -0
  16. package/dist/adapters/customVariables/ifAppManager.js +34 -0
  17. package/dist/adapters/customVariables/ifAppManager.js.map +1 -0
  18. package/dist/adapters/customVariables/ifAppMemoryMonitor.d.ts +2 -0
  19. package/dist/adapters/customVariables/ifAppMemoryMonitor.js +34 -0
  20. package/dist/adapters/customVariables/ifAppMemoryMonitor.js.map +1 -0
  21. package/dist/adapters/customVariables/ifArray.d.ts +2 -0
  22. package/dist/adapters/customVariables/ifArray.js +18 -0
  23. package/dist/adapters/customVariables/ifArray.js.map +1 -0
  24. package/dist/adapters/customVariables/ifAudioMetadata.d.ts +2 -0
  25. package/dist/adapters/customVariables/ifAudioMetadata.js +33 -0
  26. package/dist/adapters/customVariables/ifAudioMetadata.js.map +1 -0
  27. package/dist/adapters/customVariables/ifAudioResource.d.ts +2 -0
  28. package/dist/adapters/customVariables/ifAudioResource.js +34 -0
  29. package/dist/adapters/customVariables/ifAudioResource.js.map +1 -0
  30. package/dist/adapters/customVariables/ifByteArray.d.ts +2 -0
  31. package/dist/adapters/customVariables/ifByteArray.js +50 -0
  32. package/dist/adapters/customVariables/ifByteArray.js.map +1 -0
  33. package/dist/adapters/customVariables/ifCECStatus.d.ts +2 -0
  34. package/dist/adapters/customVariables/ifCECStatus.js +18 -0
  35. package/dist/adapters/customVariables/ifCECStatus.js.map +1 -0
  36. package/dist/adapters/customVariables/ifChannelStore.d.ts +2 -0
  37. package/dist/adapters/customVariables/ifChannelStore.js +34 -0
  38. package/dist/adapters/customVariables/ifChannelStore.js.map +1 -0
  39. package/dist/adapters/customVariables/ifDateTime.d.ts +2 -0
  40. package/dist/adapters/customVariables/ifDateTime.js +146 -0
  41. package/dist/adapters/customVariables/ifDateTime.js.map +1 -0
  42. package/dist/adapters/customVariables/ifDeviceInfo.d.ts +2 -0
  43. package/dist/adapters/customVariables/ifDeviceInfo.js +346 -0
  44. package/dist/adapters/customVariables/ifDeviceInfo.js.map +1 -0
  45. package/dist/adapters/customVariables/ifDraw2d.d.ts +2 -0
  46. package/dist/adapters/customVariables/ifDraw2d.js +34 -0
  47. package/dist/adapters/customVariables/ifDraw2d.js.map +1 -0
  48. package/dist/adapters/customVariables/ifEnum.d.ts +2 -0
  49. package/dist/adapters/customVariables/ifEnum.js +26 -0
  50. package/dist/adapters/customVariables/ifEnum.js.map +1 -0
  51. package/dist/adapters/customVariables/ifFileSystem.d.ts +2 -0
  52. package/dist/adapters/customVariables/ifFileSystem.js +18 -0
  53. package/dist/adapters/customVariables/ifFileSystem.js.map +1 -0
  54. package/dist/adapters/customVariables/ifFont.d.ts +2 -0
  55. package/dist/adapters/customVariables/ifFont.js +42 -0
  56. package/dist/adapters/customVariables/ifFont.js.map +1 -0
  57. package/dist/adapters/customVariables/ifFontRegistry.d.ts +2 -0
  58. package/dist/adapters/customVariables/ifFontRegistry.js +34 -0
  59. package/dist/adapters/customVariables/ifFontRegistry.js.map +1 -0
  60. package/dist/adapters/customVariables/ifGetMessagePort.d.ts +2 -0
  61. package/dist/adapters/customVariables/ifGetMessagePort.js +17 -0
  62. package/dist/adapters/customVariables/ifGetMessagePort.js.map +1 -0
  63. package/dist/adapters/customVariables/ifHdmiStatus.d.ts +2 -0
  64. package/dist/adapters/customVariables/ifHdmiStatus.js +42 -0
  65. package/dist/adapters/customVariables/ifHdmiStatus.js.map +1 -0
  66. package/dist/adapters/customVariables/ifHttpAgent.d.ts +2 -0
  67. package/dist/adapters/customVariables/ifHttpAgent.js +8 -0
  68. package/dist/adapters/customVariables/ifHttpAgent.js.map +1 -0
  69. package/dist/adapters/customVariables/ifImageMetadata.d.ts +2 -0
  70. package/dist/adapters/customVariables/ifImageMetadata.js +34 -0
  71. package/dist/adapters/customVariables/ifImageMetadata.js.map +1 -0
  72. package/dist/adapters/customVariables/ifInput.d.ts +2 -0
  73. package/dist/adapters/customVariables/ifInput.js +18 -0
  74. package/dist/adapters/customVariables/ifInput.js.map +1 -0
  75. package/dist/adapters/customVariables/ifList.d.ts +2 -0
  76. package/dist/adapters/customVariables/ifList.js +34 -0
  77. package/dist/adapters/customVariables/ifList.js.map +1 -0
  78. package/dist/adapters/customVariables/ifListToArray.d.ts +2 -0
  79. package/dist/adapters/customVariables/ifListToArray.js +18 -0
  80. package/dist/adapters/customVariables/ifListToArray.js.map +1 -0
  81. package/dist/adapters/customVariables/ifMessagePort.d.ts +2 -0
  82. package/dist/adapters/customVariables/ifMessagePort.js +18 -0
  83. package/dist/adapters/customVariables/ifMessagePort.js.map +1 -0
  84. package/dist/adapters/customVariables/ifMicrophone.d.ts +2 -0
  85. package/dist/adapters/customVariables/ifMicrophone.js +18 -0
  86. package/dist/adapters/customVariables/ifMicrophone.js.map +1 -0
  87. package/dist/adapters/customVariables/ifPath.d.ts +2 -0
  88. package/dist/adapters/customVariables/ifPath.js +26 -0
  89. package/dist/adapters/customVariables/ifPath.js.map +1 -0
  90. package/dist/adapters/customVariables/ifProgramGuide.d.ts +2 -0
  91. package/dist/adapters/customVariables/ifProgramGuide.js +18 -0
  92. package/dist/adapters/customVariables/ifProgramGuide.js.map +1 -0
  93. package/dist/adapters/customVariables/ifRegion.d.ts +2 -0
  94. package/dist/adapters/customVariables/ifRegion.js +98 -0
  95. package/dist/adapters/customVariables/ifRegion.js.map +1 -0
  96. package/dist/adapters/customVariables/ifRegistry.d.ts +2 -0
  97. package/dist/adapters/customVariables/ifRegistry.js +26 -0
  98. package/dist/adapters/customVariables/ifRegistry.js.map +1 -0
  99. package/dist/adapters/customVariables/ifRegistrySection.d.ts +2 -0
  100. package/dist/adapters/customVariables/ifRegistrySection.js +18 -0
  101. package/dist/adapters/customVariables/ifRegistrySection.js.map +1 -0
  102. package/dist/adapters/customVariables/ifSGNodeBoundingRect.d.ts +2 -0
  103. package/dist/adapters/customVariables/ifSGNodeBoundingRect.js +34 -0
  104. package/dist/adapters/customVariables/ifSGNodeBoundingRect.js.map +1 -0
  105. package/dist/adapters/customVariables/ifSGNodeChildren.d.ts +2 -0
  106. package/dist/adapters/customVariables/ifSGNodeChildren.js +74 -0
  107. package/dist/adapters/customVariables/ifSGNodeChildren.js.map +1 -0
  108. package/dist/adapters/customVariables/ifSGNodeDict.d.ts +2 -0
  109. package/dist/adapters/customVariables/ifSGNodeDict.js +18 -0
  110. package/dist/adapters/customVariables/ifSGNodeDict.js.map +1 -0
  111. package/dist/adapters/customVariables/ifSGNodeField.d.ts +2 -0
  112. package/dist/adapters/customVariables/ifSGNodeField.js +26 -0
  113. package/dist/adapters/customVariables/ifSGNodeField.js.map +1 -0
  114. package/dist/adapters/customVariables/ifSGNodeFocus.d.ts +2 -0
  115. package/dist/adapters/customVariables/ifSGNodeFocus.js +26 -0
  116. package/dist/adapters/customVariables/ifSGNodeFocus.js.map +1 -0
  117. package/dist/adapters/customVariables/ifSGNodeHttpAgentAccess.d.ts +2 -0
  118. package/dist/adapters/customVariables/ifSGNodeHttpAgentAccess.js +17 -0
  119. package/dist/adapters/customVariables/ifSGNodeHttpAgentAccess.js.map +1 -0
  120. package/dist/adapters/customVariables/ifSGScreen.d.ts +2 -0
  121. package/dist/adapters/customVariables/ifSGScreen.js +34 -0
  122. package/dist/adapters/customVariables/ifSGScreen.js.map +1 -0
  123. package/dist/adapters/customVariables/ifSocket.d.ts +2 -0
  124. package/dist/adapters/customVariables/ifSocket.js +58 -0
  125. package/dist/adapters/customVariables/ifSocket.js.map +1 -0
  126. package/dist/adapters/customVariables/ifSocketAddress.d.ts +2 -0
  127. package/dist/adapters/customVariables/ifSocketAddress.js +42 -0
  128. package/dist/adapters/customVariables/ifSocketAddress.js.map +1 -0
  129. package/dist/adapters/customVariables/ifSocketAsync.d.ts +2 -0
  130. package/dist/adapters/customVariables/ifSocketAsync.js +42 -0
  131. package/dist/adapters/customVariables/ifSocketAsync.js.map +1 -0
  132. package/dist/adapters/customVariables/ifSocketCastOption.d.ts +2 -0
  133. package/dist/adapters/customVariables/ifSocketCastOption.js +42 -0
  134. package/dist/adapters/customVariables/ifSocketCastOption.js.map +1 -0
  135. package/dist/adapters/customVariables/ifSocketConnection.d.ts +2 -0
  136. package/dist/adapters/customVariables/ifSocketConnection.js +26 -0
  137. package/dist/adapters/customVariables/ifSocketConnection.js.map +1 -0
  138. package/dist/adapters/customVariables/ifSocketConnectionOption.d.ts +2 -0
  139. package/dist/adapters/customVariables/ifSocketConnectionOption.js +42 -0
  140. package/dist/adapters/customVariables/ifSocketConnectionOption.js.map +1 -0
  141. package/dist/adapters/customVariables/ifSocketConnectionStatus.d.ts +2 -0
  142. package/dist/adapters/customVariables/ifSocketConnectionStatus.js +50 -0
  143. package/dist/adapters/customVariables/ifSocketConnectionStatus.js.map +1 -0
  144. package/dist/adapters/customVariables/ifSocketOption.d.ts +2 -0
  145. package/dist/adapters/customVariables/ifSocketOption.js +66 -0
  146. package/dist/adapters/customVariables/ifSocketOption.js.map +1 -0
  147. package/dist/adapters/customVariables/ifSocketStatus.d.ts +2 -0
  148. package/dist/adapters/customVariables/ifSocketStatus.js +90 -0
  149. package/dist/adapters/customVariables/ifSocketStatus.js.map +1 -0
  150. package/dist/adapters/customVariables/ifSourceIdentity.d.ts +2 -0
  151. package/dist/adapters/customVariables/ifSourceIdentity.js +18 -0
  152. package/dist/adapters/customVariables/ifSourceIdentity.js.map +1 -0
  153. package/dist/adapters/customVariables/ifSprite.d.ts +2 -0
  154. package/dist/adapters/customVariables/ifSprite.js +90 -0
  155. package/dist/adapters/customVariables/ifSprite.js.map +1 -0
  156. package/dist/adapters/customVariables/ifString.d.ts +2 -0
  157. package/dist/adapters/customVariables/ifString.js +26 -0
  158. package/dist/adapters/customVariables/ifString.js.map +1 -0
  159. package/dist/adapters/customVariables/ifStringOps.d.ts +2 -0
  160. package/dist/adapters/customVariables/ifStringOps.js +98 -0
  161. package/dist/adapters/customVariables/ifStringOps.js.map +1 -0
  162. package/dist/adapters/customVariables/ifTextToSpeech.d.ts +2 -0
  163. package/dist/adapters/customVariables/ifTextToSpeech.js +74 -0
  164. package/dist/adapters/customVariables/ifTextToSpeech.js.map +1 -0
  165. package/dist/adapters/customVariables/ifTextureRequest.d.ts +2 -0
  166. package/dist/adapters/customVariables/ifTextureRequest.js +26 -0
  167. package/dist/adapters/customVariables/ifTextureRequest.js.map +1 -0
  168. package/dist/adapters/customVariables/ifTimeSpan.d.ts +2 -0
  169. package/dist/adapters/customVariables/ifTimeSpan.js +34 -0
  170. package/dist/adapters/customVariables/ifTimeSpan.js.map +1 -0
  171. package/dist/adapters/customVariables/ifToStr.d.ts +2 -0
  172. package/dist/adapters/customVariables/ifToStr.js +18 -0
  173. package/dist/adapters/customVariables/ifToStr.js.map +1 -0
  174. package/dist/adapters/customVariables/ifUrlTransfer.d.ts +2 -0
  175. package/dist/adapters/customVariables/ifUrlTransfer.js +50 -0
  176. package/dist/adapters/customVariables/ifUrlTransfer.js.map +1 -0
  177. package/dist/adapters/customVariables/ifVideoPlayer.d.ts +2 -0
  178. package/dist/adapters/customVariables/ifVideoPlayer.js +34 -0
  179. package/dist/adapters/customVariables/ifVideoPlayer.js.map +1 -0
  180. package/dist/adapters/customVariables/ifXMLElement.d.ts +2 -0
  181. package/dist/adapters/customVariables/ifXMLElement.js +58 -0
  182. package/dist/adapters/customVariables/ifXMLElement.js.map +1 -0
  183. package/dist/adapters/customVariables/ifXMLList.d.ts +2 -0
  184. package/dist/adapters/customVariables/ifXMLList.js +34 -0
  185. package/dist/adapters/customVariables/ifXMLList.js.map +1 -0
  186. package/dist/adapters/customVariables/index.d.ts +78 -0
  187. package/dist/adapters/customVariables/index.js +159 -0
  188. package/dist/adapters/customVariables/index.js.map +1 -0
  189. package/dist/adapters/customVariables/roAppMemoryNotificationEvent.d.ts +2 -0
  190. package/dist/adapters/customVariables/roAppMemoryNotificationEvent.js +18 -0
  191. package/dist/adapters/customVariables/roAppMemoryNotificationEvent.js.map +1 -0
  192. package/dist/adapters/customVariables/roAudioPlayerEvent.d.ts +2 -0
  193. package/dist/adapters/customVariables/roAudioPlayerEvent.js +138 -0
  194. package/dist/adapters/customVariables/roAudioPlayerEvent.js.map +1 -0
  195. package/dist/adapters/customVariables/roCECStatusEvent.d.ts +2 -0
  196. package/dist/adapters/customVariables/roCECStatusEvent.js +34 -0
  197. package/dist/adapters/customVariables/roCECStatusEvent.js.map +1 -0
  198. package/dist/adapters/customVariables/roChannelStoreEvent.d.ts +2 -0
  199. package/dist/adapters/customVariables/roChannelStoreEvent.js +58 -0
  200. package/dist/adapters/customVariables/roChannelStoreEvent.js.map +1 -0
  201. package/dist/adapters/customVariables/roDeviceInfoEvent.d.ts +2 -0
  202. package/dist/adapters/customVariables/roDeviceInfoEvent.js +34 -0
  203. package/dist/adapters/customVariables/roDeviceInfoEvent.js.map +1 -0
  204. package/dist/adapters/customVariables/roFileSystemEvent.d.ts +2 -0
  205. package/dist/adapters/customVariables/roFileSystemEvent.js +34 -0
  206. package/dist/adapters/customVariables/roFileSystemEvent.js.map +1 -0
  207. package/dist/adapters/customVariables/roHdmiStatusEvent.d.ts +2 -0
  208. package/dist/adapters/customVariables/roHdmiStatusEvent.js +42 -0
  209. package/dist/adapters/customVariables/roHdmiStatusEvent.js.map +1 -0
  210. package/dist/adapters/customVariables/roInputEvent.d.ts +2 -0
  211. package/dist/adapters/customVariables/roInputEvent.js +26 -0
  212. package/dist/adapters/customVariables/roInputEvent.js.map +1 -0
  213. package/dist/adapters/customVariables/roMicrophoneEvent.d.ts +2 -0
  214. package/dist/adapters/customVariables/roMicrophoneEvent.js +34 -0
  215. package/dist/adapters/customVariables/roMicrophoneEvent.js.map +1 -0
  216. package/dist/adapters/customVariables/roSGNodeEvent.d.ts +2 -0
  217. package/dist/adapters/customVariables/roSGNodeEvent.js +50 -0
  218. package/dist/adapters/customVariables/roSGNodeEvent.js.map +1 -0
  219. package/dist/adapters/customVariables/roSGScreenEvent.d.ts +2 -0
  220. package/dist/adapters/customVariables/roSGScreenEvent.js +18 -0
  221. package/dist/adapters/customVariables/roSGScreenEvent.js.map +1 -0
  222. package/dist/adapters/customVariables/roSocketEvent.d.ts +2 -0
  223. package/dist/adapters/customVariables/roSocketEvent.js +18 -0
  224. package/dist/adapters/customVariables/roSocketEvent.js.map +1 -0
  225. package/dist/adapters/customVariables/roSystemLogEvent.d.ts +2 -0
  226. package/dist/adapters/customVariables/roSystemLogEvent.js +18 -0
  227. package/dist/adapters/customVariables/roSystemLogEvent.js.map +1 -0
  228. package/dist/adapters/customVariables/roTextToSpeechEvent.d.ts +2 -0
  229. package/dist/adapters/customVariables/roTextToSpeechEvent.js +34 -0
  230. package/dist/adapters/customVariables/roTextToSpeechEvent.js.map +1 -0
  231. package/dist/adapters/customVariables/roTextureRequestEvent.d.ts +2 -0
  232. package/dist/adapters/customVariables/roTextureRequestEvent.js +42 -0
  233. package/dist/adapters/customVariables/roTextureRequestEvent.js.map +1 -0
  234. package/dist/adapters/customVariables/roUniversalControlEvent.d.ts +2 -0
  235. package/dist/adapters/customVariables/roUniversalControlEvent.js +58 -0
  236. package/dist/adapters/customVariables/roUniversalControlEvent.js.map +1 -0
  237. package/dist/adapters/customVariables/roUrlEvent.d.ts +2 -0
  238. package/dist/adapters/customVariables/roUrlEvent.js +66 -0
  239. package/dist/adapters/customVariables/roUrlEvent.js.map +1 -0
  240. package/dist/adapters/customVariables/roVideoPlayerEvent.d.ts +2 -0
  241. package/dist/adapters/customVariables/roVideoPlayerEvent.js +154 -0
  242. package/dist/adapters/customVariables/roVideoPlayerEvent.js.map +1 -0
  243. package/dist/adapters/customVariables/utils.d.ts +6 -0
  244. package/dist/adapters/customVariables/utils.js +32 -0
  245. package/dist/adapters/customVariables/utils.js.map +1 -0
  246. package/dist/debugProtocol/client/DebugProtocolClient.js +5 -6
  247. package/dist/debugProtocol/client/DebugProtocolClient.js.map +1 -1
  248. package/dist/debugSession/BrightScriptDebugSession.d.ts +30 -0
  249. package/dist/debugSession/BrightScriptDebugSession.js +460 -84
  250. package/dist/debugSession/BrightScriptDebugSession.js.map +1 -1
  251. package/dist/interfaces.d.ts +1 -0
  252. package/dist/managers/ProjectManager.d.ts +2 -1
  253. package/dist/managers/ProjectManager.js +4 -3
  254. package/dist/managers/ProjectManager.js.map +1 -1
  255. package/dist/managers/SourceMapManager.d.ts +3 -0
  256. package/dist/managers/SourceMapManager.js +46 -29
  257. package/dist/managers/SourceMapManager.js.map +1 -1
  258. package/dist/util.js +1 -2
  259. package/dist/util.js.map +1 -1
  260. package/package.json +3 -3
  261. package/roku-debug-0.21.17.tgz +0 -0
  262. package/roku-debug-0.21.15.tgz +0 -0
@@ -22,11 +22,13 @@ const BreakpointManager_1 = require("../managers/BreakpointManager");
22
22
  const logging_1 = require("../logging");
23
23
  const VariablesResponse_1 = require("../debugProtocol/events/responses/VariablesResponse");
24
24
  const brighterscript_1 = require("brighterscript");
25
+ const debounce_1 = require("debounce");
25
26
  const diagnosticSource = 'roku-debug';
26
27
  class BrightScriptDebugSession extends debugadapter_1.DebugSession {
27
28
  constructor() {
28
29
  super();
29
30
  this.logger = logging_1.logger.createLogger(`[session]`);
31
+ this.isWindowsPlatform = process.platform.startsWith('win');
30
32
  /**
31
33
  * A sequence used to help identify log statements for requests
32
34
  */
@@ -49,6 +51,10 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
49
51
  */
50
52
  this.COMPILE_ERROR_THREAD_ID = 7777;
51
53
  this.exceptionBreakpoints = [];
54
+ this.pendingSendLogPromise = Promise.resolve();
55
+ this.debounceSendInvalidatedEvent = (0, debounce_1.debounce)((threadId, frameId) => {
56
+ this.sendInvalidatedEvent(threadId, frameId);
57
+ }, 50);
52
58
  this.evaluateRequestPromise = Promise.resolve();
53
59
  this.evaluateVarIndexByFrameId = new Map();
54
60
  /**
@@ -194,6 +200,13 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
194
200
  this.sendResponse(response);
195
201
  }
196
202
  }
203
+ async setTransientsToInvalid() {
204
+ let brsErr = Object.values(this.variables).find((v) => v.name === '__brs_err__');
205
+ if (brsErr && brsErr.type !== VariablesResponse_1.VariableType.Uninitialized) {
206
+ // Assigning the variable to the function call results in it becoming unintialized
207
+ await this.rokuAdapter.evaluate(`__brs_err__ = [].clear()`, brsErr.frameId);
208
+ }
209
+ }
197
210
  showPopupMessage(message, severity, modal = false) {
198
211
  this.logger.trace('[showPopupMessage]', severity, message);
199
212
  this.sendEvent(new Events_1.PopupMessageEvent(message, severity, modal));
@@ -232,7 +245,7 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
232
245
  * @returns
233
246
  */
234
247
  normalizeLaunchConfig(config) {
235
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
248
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
236
249
  (_a = config.cwd) !== null && _a !== void 0 ? _a : (config.cwd = process.cwd());
237
250
  (_b = config.outDir) !== null && _b !== void 0 ? _b : (config.outDir = (0, FileUtils_1.standardizePath) `${config.cwd}/out`);
238
251
  (_c = config.stagingDir) !== null && _c !== void 0 ? _c : (config.stagingDir = (0, FileUtils_1.standardizePath) `${config.outDir}/.roku-deploy-staging`);
@@ -244,6 +257,8 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
244
257
  (_j = config.brightScriptConsolePort) !== null && _j !== void 0 ? _j : (config.brightScriptConsolePort = 8085);
245
258
  (_k = config.stagingDir) !== null && _k !== void 0 ? _k : (config.stagingDir = config.stagingFolderPath);
246
259
  (_l = config.emitChannelPublishedEvent) !== null && _l !== void 0 ? _l : (config.emitChannelPublishedEvent = true);
260
+ (_m = config.rewriteDevicePathsInLogs) !== null && _m !== void 0 ? _m : (config.rewriteDevicePathsInLogs = true);
261
+ (_o = config.autoResolveVirtualVariables) !== null && _o !== void 0 ? _o : (config.autoResolveVirtualVariables = false);
247
262
  return config;
248
263
  }
249
264
  async launchRequest(response, config) {
@@ -305,12 +320,12 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
305
320
  //pass along the console output
306
321
  if (this.launchConfiguration.consoleOutput === 'full') {
307
322
  this.rokuAdapter.on('console-output', (data) => {
308
- this.sendLogOutput(data);
323
+ void this.sendLogOutput(data);
309
324
  });
310
325
  }
311
326
  else {
312
327
  this.rokuAdapter.on('unhandled-console-output', (data) => {
313
- this.sendLogOutput(data);
328
+ void this.sendLogOutput(data);
314
329
  });
315
330
  }
316
331
  // Send chanperf events to the extension
@@ -536,15 +551,125 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
536
551
  */
537
552
  sendLogOutput(logOutput) {
538
553
  this.fileLoggingManager.writeRokuDeviceLog(logOutput);
539
- const lines = logOutput.split(/\r?\n/g);
540
- for (let i = 0; i < lines.length; i++) {
541
- let line = lines[i];
542
- if (i < lines.length - 1) {
543
- line += '\n';
554
+ this.pendingSendLogPromise = this.pendingSendLogPromise.then(async () => {
555
+ logOutput = await this.convertBacktracePaths(logOutput);
556
+ const lines = logOutput.split(/\r?\n/g);
557
+ for (let i = 0; i < lines.length; i++) {
558
+ let line = lines[i];
559
+ if (i < lines.length - 1) {
560
+ line += '\n';
561
+ }
562
+ if (this.launchConfiguration.rewriteDevicePathsInLogs) {
563
+ let potentialPaths = this.getPotentialPkgPaths(line);
564
+ for (let potentialPath of potentialPaths) {
565
+ let originalLocation = await this.projectManager.getSourceLocation(potentialPath.path, potentialPath.lineNumber, potentialPath.columnNumber);
566
+ if (originalLocation) {
567
+ let replacement;
568
+ replacement = originalLocation.filePath.replaceAll(' ', '%20');
569
+ if (replacement !== originalLocation.filePath) {
570
+ if (this.isWindowsPlatform) {
571
+ replacement = `vscode://file/${replacement}`;
572
+ }
573
+ else {
574
+ replacement = `file://${replacement}`;
575
+ }
576
+ }
577
+ replacement += `:${originalLocation.lineNumber}`;
578
+ if (potentialPath.columnNumber !== undefined) {
579
+ replacement += `:${originalLocation.columnIndex + 1}`;
580
+ }
581
+ line = line.replaceAll(potentialPath.fullMatch, replacement);
582
+ }
583
+ }
584
+ }
585
+ this.sendEvent(new debugadapter_1.OutputEvent(line, 'stdout'));
586
+ this.sendEvent(new Events_1.LogOutputEvent(line));
587
+ }
588
+ });
589
+ return this.pendingSendLogPromise;
590
+ }
591
+ /**
592
+ * Extracts potential package paths from a given line of text.
593
+ *
594
+ * This method uses a regular expression to find matches in the provided line
595
+ * and returns an array of objects containing details about each match.
596
+ *
597
+ * @param input - The line of text to search for potential package paths.
598
+ * @returns An array of objects, each containing:
599
+ * - `fullMatch`: The full matched string.
600
+ * - `path`: The extracted path from the match.
601
+ * - `lineNumber`: The line number extracted from the match.
602
+ * - `columnNumber`: The column number extracted from the match, or `undefined` if not found.
603
+ */
604
+ getPotentialPkgPaths(input) {
605
+ var _a, _b;
606
+ // https://regex101.com/r/ixpQiq/1
607
+ let matches = input.matchAll(/((?:\.\.\.|[A-Za-z_0-9]*pkg\:\/)[A-Za-z_0-9 \/\.]+\.[A-Za-z_0-9 \/]+)(?:(?:\:)(\d+)(?:\:(\d+))?|\((\d+)(?:\:(\d+))?\))/ig);
608
+ let paths = [];
609
+ if (matches) {
610
+ for (let match of matches) {
611
+ let fullMatch = match[0];
612
+ let path = match[1];
613
+ let lineNumber = parseInt((_a = match[2]) !== null && _a !== void 0 ? _a : match[4]);
614
+ let columnNumber = parseInt((_b = match[3]) !== null && _b !== void 0 ? _b : match[5]);
615
+ if (isNaN(columnNumber)) {
616
+ columnNumber = undefined;
617
+ }
618
+ paths.push({
619
+ fullMatch: fullMatch,
620
+ path: path,
621
+ lineNumber: lineNumber,
622
+ columnNumber: columnNumber
623
+ });
544
624
  }
545
- this.sendEvent(new debugadapter_1.OutputEvent(line, 'stdout'));
546
- this.sendEvent(new Events_1.LogOutputEvent(line));
547
625
  }
626
+ return paths;
627
+ }
628
+ /**
629
+ * Converts the filename property in backtrace objects in the given input string to source paths if found
630
+ */
631
+ async convertBacktracePaths(input) {
632
+ if (!this.launchConfiguration.rewriteDevicePathsInLogs) {
633
+ return input;
634
+ }
635
+ // Why does this not work? It should work, but it doesn't. I'm not sure why.
636
+ // let matches = input.matchAll(this.deviceBacktraceObjectRegex);
637
+ // https://regex101.com/r/y1koaV/2
638
+ let deviceBacktraceObjectRegex = /{\s+filename:\s+"([A-Za-z0-9_\.\/\: ]+)"\s+function\:\s+".+"\s+(line_number\:\s+(\d+))\s+}/gi;
639
+ let matches = [];
640
+ let match = deviceBacktraceObjectRegex.exec(input);
641
+ while (match) {
642
+ matches.push(match);
643
+ match = deviceBacktraceObjectRegex.exec(input);
644
+ }
645
+ if (matches) {
646
+ for (let match of matches) {
647
+ let fullMatch = match[0];
648
+ let filePath = match[1];
649
+ let fullLineNumber = match[2];
650
+ let lineNumber = parseInt(match[3]);
651
+ let originalLocation = await this.projectManager.getSourceLocation(filePath, lineNumber);
652
+ if (originalLocation) {
653
+ let fileReplacement;
654
+ fileReplacement = originalLocation.filePath.replaceAll(' ', '%20');
655
+ if (fileReplacement !== originalLocation.filePath) {
656
+ if (this.isWindowsPlatform) {
657
+ fileReplacement = `vscode://file/${fileReplacement}`;
658
+ }
659
+ else {
660
+ fileReplacement = `file://${fileReplacement}`;
661
+ }
662
+ }
663
+ fileReplacement += `:${originalLocation.lineNumber}`;
664
+ let lineNumberReplacement = fullLineNumber.replace(lineNumber.toString(), originalLocation.lineNumber.toString());
665
+ // replace the full backtrace object with the an updated version so we don't modify other parts of the log output that might contain the same file path
666
+ let completeReplacement = fullMatch.replace(filePath, fileReplacement);
667
+ completeReplacement = completeReplacement.replace(fullLineNumber, lineNumberReplacement);
668
+ input = input.replaceAll(fullMatch, completeReplacement);
669
+ }
670
+ }
671
+ }
672
+ return input;
548
673
  }
549
674
  async runAutomaticSceneGraphCommands(commands) {
550
675
  if (commands) {
@@ -848,7 +973,7 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
848
973
  if (!result) {
849
974
  throw new Error(`Could not get scopes`);
850
975
  }
851
- v = this.getVariableFromResult(result, args.frameId);
976
+ v = await this.getVariableFromResult(result, args.frameId);
852
977
  //TODO - testing something, remove later
853
978
  // eslint-disable-next-line camelcase
854
979
  v.request_seq = response.request_seq;
@@ -880,6 +1005,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
880
1005
  return;
881
1006
  }
882
1007
  this.logger.log('continueRequest');
1008
+ await this.setTransientsToInvalid(); // call before clearState
1009
+ this.clearState();
1010
+ // The debug session ends after the next line. Do not put new work after this line.
883
1011
  await this.rokuAdapter.continue();
884
1012
  this.sendResponse(response);
885
1013
  }
@@ -911,6 +1039,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
911
1039
  await this.shutdown();
912
1040
  return;
913
1041
  }
1042
+ await this.setTransientsToInvalid(); // call before clearState
1043
+ this.clearState();
1044
+ // The debug session ends after the next line. Do not put new work after this line.
914
1045
  try {
915
1046
  await this.rokuAdapter.stepOver(args.threadId);
916
1047
  this.logger.info('[nextRequest] end');
@@ -928,6 +1059,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
928
1059
  await this.shutdown();
929
1060
  return;
930
1061
  }
1062
+ await this.setTransientsToInvalid(); // call before clearState
1063
+ this.clearState();
1064
+ // The debug session ends after the next line. Do not put new work after this line.
931
1065
  await this.rokuAdapter.stepInto(args.threadId);
932
1066
  this.sendResponse(response);
933
1067
  this.logger.info('[stepInRequest] end');
@@ -940,6 +1074,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
940
1074
  await this.shutdown();
941
1075
  return;
942
1076
  }
1077
+ await this.setTransientsToInvalid(); // call before clearState
1078
+ this.clearState();
1079
+ // The debug session ends after the next line. Do not put new work after this line.
943
1080
  await this.rokuAdapter.stepOut(args.threadId);
944
1081
  this.sendResponse(response);
945
1082
  this.logger.info('[stepOutRequest] end');
@@ -950,13 +1087,15 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
950
1087
  this.logger.info('[stepBackRequest] end');
951
1088
  }
952
1089
  async variablesRequest(response, args) {
953
- var _a, _b;
1090
+ var _a, _b, _c, _d;
954
1091
  const logger = this.logger.createLogger('[variablesRequest]');
1092
+ let sendInvalidatedEvent = false;
1093
+ let frameId = null;
955
1094
  try {
956
1095
  logger.log('begin', { args });
957
1096
  //ensure the rokuAdapter is loaded
958
1097
  await this.getRokuAdapter();
959
- let childVariables = [];
1098
+ let updatedVariables = [];
960
1099
  //wait for any `evaluate` commands to finish so we have a higher likely hood of being at a debugger prompt
961
1100
  await this.evaluateRequestPromise;
962
1101
  if (((_a = this.rokuAdapter) === null || _a === void 0 ? void 0 : _a.isAtDebuggerPrompt) !== true) {
@@ -974,13 +1113,15 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
974
1113
  for (const varName of vars) {
975
1114
  let { evalArgs } = await this.evaluateExpressionToTempVar({ expression: varName, frameId: -1 }, util_1.util.getVariablePath(varName));
976
1115
  let result = await this.rokuAdapter.getVariable(evalArgs.expression, -1);
977
- let tempVar = this.getVariableFromResult(result, -1);
978
- childVariables.push(tempVar);
1116
+ let tempVar = await this.getVariableFromResult(result, -1);
1117
+ updatedVariables.push(tempVar);
979
1118
  }
980
1119
  }
981
1120
  else {
982
- childVariables.push(new debugadapter_1.Variable('variables disabled by launch.json setting', 'enableVariablesPanel: false'));
1121
+ updatedVariables.push(new debugadapter_1.Variable('variables disabled by launch.json setting', 'enableVariablesPanel: false'));
983
1122
  }
1123
+ this.variables[args.variablesReference] = new debugadapter_1.Variable('', '', args.variablesReference, 0, updatedVariables.length);
1124
+ this.variables[args.variablesReference].childVariables = updatedVariables;
984
1125
  }
985
1126
  else {
986
1127
  //find the variable with this reference
@@ -991,35 +1132,120 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
991
1132
  return this.sendResponse(response);
992
1133
  }
993
1134
  logger.log('variable', v);
994
- //query for child vars if we haven't done it yet.
995
- if (v.childVariables.length === 0) {
996
- let { evalArgs } = await this.evaluateExpressionToTempVar({ expression: v.evaluateName, frameId: v.frameId }, util_1.util.getVariablePath(v.evaluateName));
997
- let result = await this.rokuAdapter.getVariable(evalArgs.expression, v.frameId);
998
- let tempVar = this.getVariableFromResult(result, v.frameId);
999
- tempVar.frameId = v.frameId;
1000
- v.childVariables = tempVar.childVariables;
1135
+ //query for child vars if we haven't done it yet or DAP is asking to resolve a lazy variable
1136
+ if (v.childVariables.length === 0 || v.isResolved) {
1137
+ let tempVar;
1138
+ if (!v.isResolved) {
1139
+ // Evaluate the variable
1140
+ try {
1141
+ let { evalArgs } = await this.evaluateExpressionToTempVar({ expression: v.evaluateName, frameId: v.frameId }, util_1.util.getVariablePath(v.evaluateName));
1142
+ let result = await this.rokuAdapter.getVariable(evalArgs.expression, v.frameId);
1143
+ tempVar = await this.getVariableFromResult(result, v.frameId);
1144
+ tempVar.frameId = v.frameId;
1145
+ // Determine if the variable has changed
1146
+ sendInvalidatedEvent = v.type !== tempVar.type || v.indexedVariables !== tempVar.indexedVariables;
1147
+ }
1148
+ catch (error) {
1149
+ logger.error('Error getting variables', error);
1150
+ tempVar = new debugadapter_1.Variable('Error', `❌ Error: ${error.message}`);
1151
+ tempVar.type = '';
1152
+ tempVar.childVariables = [];
1153
+ sendInvalidatedEvent = true;
1154
+ response.success = false;
1155
+ response.message = error.message;
1156
+ }
1157
+ // Merge the resulting updates together
1158
+ v.childVariables = tempVar.childVariables;
1159
+ v.value = tempVar.value;
1160
+ v.type = tempVar.type;
1161
+ v.indexedVariables = tempVar.indexedVariables;
1162
+ v.namedVariables = tempVar.namedVariables;
1163
+ }
1164
+ frameId = v.frameId;
1165
+ if (((_b = v === null || v === void 0 ? void 0 : v.presentationHint) === null || _b === void 0 ? void 0 : _b.lazy) || v.isResolved) {
1166
+ // If this was a lazy variable we need to respond with the updated variable and not the children
1167
+ if (v.isResolved && v.childVariables.length > 0) {
1168
+ updatedVariables = v.childVariables;
1169
+ }
1170
+ else {
1171
+ updatedVariables = [v];
1172
+ }
1173
+ v.isResolved = true;
1174
+ }
1175
+ else {
1176
+ updatedVariables = v.childVariables;
1177
+ }
1178
+ // If the variable has no children, set the reference to 0
1179
+ // so it does not look expandable in the Ui
1180
+ if (v.childVariables.length === 0) {
1181
+ v.variablesReference = 0;
1182
+ }
1183
+ // If the variable was resolve in the past we may not have fetched a new temp var
1184
+ tempVar !== null && tempVar !== void 0 ? tempVar : (tempVar = v);
1185
+ if (v === null || v === void 0 ? void 0 : v.presentationHint) {
1186
+ v.presentationHint.lazy = (_c = tempVar.presentationHint) === null || _c === void 0 ? void 0 : _c.lazy;
1187
+ }
1188
+ else {
1189
+ v.presentationHint = tempVar.presentationHint;
1190
+ }
1191
+ }
1192
+ else {
1193
+ updatedVariables = v.childVariables;
1001
1194
  }
1002
- childVariables = v.childVariables;
1003
- }
1004
- //if the variable is an array, send only the requested range
1005
- if (Array.isArray(childVariables) && args.filter === 'indexed') {
1006
- //only send the variable range requested by the debugger
1007
- childVariables = childVariables.slice(args.start, args.start + args.count);
1008
1195
  }
1009
- let filteredChildVariables = this.launchConfiguration.showHiddenVariables !== true ? childVariables.filter((child) => !child.name.startsWith(this.tempVarPrefix)) : childVariables;
1010
1196
  response.body = {
1011
- variables: filteredChildVariables
1197
+ variables: this.filterVariablesUpdates(updatedVariables, args, this.variables[args.variablesReference])
1012
1198
  };
1013
1199
  }
1014
1200
  catch (error) {
1015
1201
  logger.error('Error during variablesRequest', error, { args });
1016
1202
  response.success = false;
1017
- response.message = (_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : 'Error during variablesRequest';
1203
+ response.message = (_d = error === null || error === void 0 ? void 0 : error.message) !== null && _d !== void 0 ? _d : 'Error during variablesRequest';
1018
1204
  }
1019
1205
  finally {
1020
1206
  logger.info('end', { response });
1021
1207
  }
1022
1208
  this.sendResponse(response);
1209
+ if (sendInvalidatedEvent) {
1210
+ this.debounceSendInvalidatedEvent(null, frameId);
1211
+ }
1212
+ }
1213
+ filterVariablesUpdates(updatedVariables, args, v) {
1214
+ var _a;
1215
+ if (!updatedVariables || !v) {
1216
+ return [];
1217
+ }
1218
+ let start = (_a = args.start) !== null && _a !== void 0 ? _a : 0;
1219
+ //if the variable is an array, send only the requested range
1220
+ if (Array.isArray(updatedVariables) && args.filter === 'indexed') {
1221
+ //only send the variable range requested by the debugger
1222
+ if (!args.count) {
1223
+ updatedVariables = updatedVariables.slice(0, v.indexedVariables);
1224
+ }
1225
+ else {
1226
+ updatedVariables = updatedVariables.slice(start, start + args.count);
1227
+ }
1228
+ }
1229
+ if (Array.isArray(updatedVariables) && args.filter === 'named') {
1230
+ // We currently do not support named variable paging so we always send all named variables
1231
+ updatedVariables = updatedVariables.slice(v.indexedVariables);
1232
+ }
1233
+ let filteredUpdatedVariables = this.launchConfiguration.showHiddenVariables !== true ? updatedVariables.filter((child) => !child.name.startsWith(this.tempVarPrefix)) : updatedVariables;
1234
+ if (this.launchConfiguration.showHiddenVariables !== true) {
1235
+ filteredUpdatedVariables = filteredUpdatedVariables.filter((child) => {
1236
+ //A transient variable that we show when there is a value
1237
+ if (child.name === '__brs_err__' && child.type !== VariablesResponse_1.VariableType.Uninitialized) {
1238
+ return true;
1239
+ }
1240
+ else if (util_1.util.isTransientVariable(child.name)) {
1241
+ return false;
1242
+ }
1243
+ else {
1244
+ return true;
1245
+ }
1246
+ });
1247
+ }
1248
+ return filteredUpdatedVariables;
1023
1249
  }
1024
1250
  getNextVarIndex(frameId) {
1025
1251
  if (!this.evaluateVarIndexByFrameId.has(frameId)) {
@@ -1065,7 +1291,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1065
1291
  }
1066
1292
  //is at debugger prompt
1067
1293
  }
1068
- else {
1294
+ else if (args.expression.trim()) {
1295
+ // We trim and check that the expression is not an empty string so that we do not send empty expressions to the Roku
1296
+ // This happens mostly when hovering over leading whitespace in the editor
1069
1297
  let { evalArgs, variablePath } = await this.evaluateExpressionToTempVar(args, util_1.util.getVariablePath(args.expression));
1070
1298
  //if we found a variable path (e.g. ['a', 'b', 'c']) then do a variable lookup because it's faster and more widely supported than `evaluate`
1071
1299
  if (variablePath) {
@@ -1080,7 +1308,7 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1080
1308
  if (!result) {
1081
1309
  throw new Error('Error: unable to evaluate expression');
1082
1310
  }
1083
- v = this.getVariableFromResult(result, evalArgs.frameId);
1311
+ v = await this.getVariableFromResult(result, evalArgs.frameId);
1084
1312
  //TODO - testing something, remove later
1085
1313
  // eslint-disable-next-line camelcase
1086
1314
  v.request_seq = response.request_seq;
@@ -1098,14 +1326,20 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1098
1326
  else {
1099
1327
  let commandResults = await this.rokuAdapter.evaluate(evalArgs.expression, evalArgs.frameId);
1100
1328
  commandResults.message = util_1.util.trimDebugPrompt(commandResults.message);
1101
- if (args.context !== 'watch') {
1102
- //clear variable cache since this action could have side-effects
1329
+ if (args.context === 'repl') {
1330
+ // Clear variable cache since this action could have side-effects
1331
+ // Only do this for REPL requests as hovers and watches should not clear the cache
1103
1332
  this.clearState();
1104
1333
  this.sendInvalidatedEvent(null, evalArgs.frameId);
1105
1334
  }
1106
- //if the adapter captured output (probably only telnet), print it to the vscode debug console
1335
+ // If the adapter captured output (probably only telnet), log the results
1107
1336
  if (typeof commandResults.message === 'string') {
1108
- this.sendEvent(new debugadapter_1.OutputEvent(commandResults.message, commandResults.type === 'error' ? 'stderr' : 'stdio'));
1337
+ this.logger.debug('evaluateRequest', { commandResults });
1338
+ if (args.context === 'repl') {
1339
+ // If the command was a repl command, send the output to the debug console for the developer as well
1340
+ // We limit this to repl only so you don't get extra logs when hovering over variables ro running watches
1341
+ this.sendEvent(new debugadapter_1.OutputEvent(commandResults.message, commandResults.type === 'error' ? 'stderr' : 'stdio'));
1342
+ }
1109
1343
  }
1110
1344
  if (this.enableDebugProtocol || (typeof commandResults.message !== 'string')) {
1111
1345
  response.body = {
@@ -1138,13 +1372,14 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1138
1372
  if (!variablePath && util_1.util.isAssignableExpression(args.expression)) {
1139
1373
  let varIndex = this.getNextVarIndex(args.frameId);
1140
1374
  let arrayVarName = this.tempVarPrefix + 'eval';
1375
+ let command = '';
1141
1376
  if (varIndex === 0) {
1142
- const response = await this.rokuAdapter.evaluate(`${arrayVarName} = []`, args.frameId);
1143
- console.log(response);
1377
+ command += `if type(${arrayVarName}) = "<uninitialized>" then ${arrayVarName} = []\n`;
1144
1378
  }
1145
1379
  let statement = `${arrayVarName}[${varIndex}] = ${args.expression}`;
1146
1380
  returnVal.evalArgs.expression = `${arrayVarName}[${varIndex}]`;
1147
- let commandResults = await this.rokuAdapter.evaluate(statement, args.frameId);
1381
+ command += statement;
1382
+ let commandResults = await this.rokuAdapter.evaluate(command, args.frameId);
1148
1383
  if (commandResults.type === 'error') {
1149
1384
  throw new Error(commandResults.message);
1150
1385
  }
@@ -1152,6 +1387,49 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1152
1387
  }
1153
1388
  return returnVal;
1154
1389
  }
1390
+ async bulkEvaluateExpressionToTempVar(frameId, argsArray, variablePathArray) {
1391
+ let results = {
1392
+ evaluations: [],
1393
+ bulkVarName: ''
1394
+ };
1395
+ let storedVariables = [];
1396
+ let command = '';
1397
+ for (let i = 0; i < argsArray.length; i++) {
1398
+ let args = argsArray[i];
1399
+ let variablePath = variablePathArray[i];
1400
+ let returnVal = { evalArgs: args, variablePath };
1401
+ if (!variablePath && util_1.util.isAssignableExpression(args.expression)) {
1402
+ let varIndex = this.getNextVarIndex(frameId);
1403
+ let arrayVarName = this.tempVarPrefix + 'eval';
1404
+ if (varIndex === 0) {
1405
+ command += `if type(${arrayVarName}) = "<uninitialized>" then ${arrayVarName} = []\n`;
1406
+ }
1407
+ let statement = `${arrayVarName}[${varIndex}] = ${args.expression}\n`;
1408
+ returnVal.evalArgs.expression = `${arrayVarName}[${varIndex}]`;
1409
+ command += statement;
1410
+ storedVariables.push(`${arrayVarName}[${varIndex}]`);
1411
+ returnVal.variablePath = [arrayVarName, varIndex.toString()];
1412
+ }
1413
+ results.evaluations[i] = returnVal;
1414
+ }
1415
+ if (command) {
1416
+ // create a bulk container for the command results
1417
+ let varIndex = this.getNextVarIndex(frameId);
1418
+ let arrayVarName = this.tempVarPrefix + 'eval';
1419
+ let bulkContainerStatement = `${arrayVarName}[${varIndex}] = [\n`;
1420
+ for (let storedVariable of storedVariables) {
1421
+ bulkContainerStatement += `${storedVariable},\n`;
1422
+ }
1423
+ bulkContainerStatement += `]`;
1424
+ command += bulkContainerStatement;
1425
+ results.bulkVarName = `${arrayVarName}[${varIndex}]`;
1426
+ let commandResults = await this.rokuAdapter.evaluate(command, frameId);
1427
+ if (commandResults.type === 'error') {
1428
+ throw new Error(commandResults.message);
1429
+ }
1430
+ }
1431
+ return results;
1432
+ }
1155
1433
  /**
1156
1434
  * Called when the host stops debugging
1157
1435
  * @param response
@@ -1202,8 +1480,8 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1202
1480
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
1203
1481
  this.rokuAdapter.on('runtime-error', async (exception) => {
1204
1482
  var _a;
1205
- let rokuAdapter = await this.getRokuAdapter();
1206
- let threads = await rokuAdapter.getThreads();
1483
+ await this.getRokuAdapter();
1484
+ const threads = await this.setupSuspendedState();
1207
1485
  let threadId = (_a = threads[0]) === null || _a === void 0 ? void 0 : _a.threadId;
1208
1486
  this.sendEvent(new debugadapter_1.StoppedEvent(Events_1.StoppedEventReason.exception, threadId, exception.message));
1209
1487
  });
@@ -1217,11 +1495,31 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1217
1495
  return this.rokuAdapter;
1218
1496
  }
1219
1497
  async onSuspend() {
1220
- var _a, _b;
1498
+ var _a;
1499
+ const threads = await this.setupSuspendedState();
1500
+ const activeThread = threads.find(x => x.isSelected);
1501
+ //if !stopOnEntry, and we haven't encountered a suspend yet, THIS is the entry breakpoint. auto-continue
1502
+ if (!this.entryBreakpointWasHandled && !this.launchConfiguration.stopOnEntry) {
1503
+ this.entryBreakpointWasHandled = true;
1504
+ //if there's a user-defined breakpoint at this exact position, it needs to be handled like a regular breakpoint (i.e. suspend). So only auto-continue if there's no breakpoint here
1505
+ if (activeThread && !await this.breakpointManager.lineHasBreakpoint(this.projectManager.getAllProjects(), activeThread.filePath, activeThread.lineNumber - 1)) {
1506
+ this.logger.info('Encountered entry breakpoint and `stopOnEntry` is disabled. Continuing...');
1507
+ return this.rokuAdapter.continue();
1508
+ }
1509
+ }
1510
+ const event = new debugadapter_1.StoppedEvent(Events_1.StoppedEventReason.breakpoint,
1511
+ //Not sure why, but sometimes there is no active thread. Just pick thread 0 to prevent the app from totally crashing
1512
+ (_a = activeThread === null || activeThread === void 0 ? void 0 : activeThread.threadId) !== null && _a !== void 0 ? _a : 0, '' //exception text
1513
+ );
1514
+ // Socket debugger will always stop all threads and supports multi thread inspection.
1515
+ event.body.allThreadsStopped = this.enableDebugProtocol;
1516
+ this.sendEvent(event);
1517
+ }
1518
+ async setupSuspendedState() {
1519
+ var _a;
1221
1520
  //clear the index for storing evalutated expressions
1222
1521
  this.evaluateVarIndexByFrameId.clear();
1223
1522
  const threads = await this.rokuAdapter.getThreads();
1224
- const activeThread = threads.find(x => x.isSelected);
1225
1523
  //TODO remove this once Roku fixes their threads off-by-one line number issues
1226
1524
  //look up the correct line numbers for each thread from the StackTrace
1227
1525
  await Promise.all(threads.map(async (thread) => {
@@ -1248,46 +1546,60 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1248
1546
  //sync breakpoints
1249
1547
  await ((_a = this.rokuAdapter) === null || _a === void 0 ? void 0 : _a.syncBreakpoints());
1250
1548
  this.logger.info('received "suspend" event from adapter');
1251
- //if !stopOnEntry, and we haven't encountered a suspend yet, THIS is the entry breakpoint. auto-continue
1252
- if (!this.entryBreakpointWasHandled && !this.launchConfiguration.stopOnEntry) {
1253
- this.entryBreakpointWasHandled = true;
1254
- //if there's a user-defined breakpoint at this exact position, it needs to be handled like a regular breakpoint (i.e. suspend). So only auto-continue if there's no breakpoint here
1255
- if (activeThread && !await this.breakpointManager.lineHasBreakpoint(this.projectManager.getAllProjects(), activeThread.filePath, activeThread.lineNumber - 1)) {
1256
- this.logger.info('Encountered entry breakpoint and `stopOnEntry` is disabled. Continuing...');
1257
- return this.rokuAdapter.continue();
1258
- }
1259
- }
1260
1549
  this.clearState();
1261
- const event = new debugadapter_1.StoppedEvent(Events_1.StoppedEventReason.breakpoint,
1262
- //Not sure why, but sometimes there is no active thread. Just pick thread 0 to prevent the app from totally crashing
1263
- (_b = activeThread === null || activeThread === void 0 ? void 0 : activeThread.threadId) !== null && _b !== void 0 ? _b : 0, '' //exception text
1264
- );
1265
- // Socket debugger will always stop all threads and supports multi thread inspection.
1266
- event.body.allThreadsStopped = this.enableDebugProtocol;
1267
- this.sendEvent(event);
1550
+ return threads;
1268
1551
  }
1269
- getVariableFromResult(result, frameId) {
1270
- var _a, _b, _c, _d, _e;
1552
+ async getVariableFromResult(result, frameId, maxDepth = 1) {
1553
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1271
1554
  let v;
1272
1555
  if (result) {
1273
1556
  if (this.enableDebugProtocol) {
1274
1557
  let refId = this.getEvaluateRefId(result.evaluateName, frameId);
1558
+ if (result.isCustom && !((_a = result.presentationHint) === null || _a === void 0 ? void 0 : _a.lazy) && result.evaluateNow) {
1559
+ try {
1560
+ // We should not wait to resolve this variable later. Fetch, store, and merge the results right away.
1561
+ let { evalArgs } = await this.evaluateExpressionToTempVar({ expression: result.evaluateName, frameId: frameId }, util_1.util.getVariablePath(result.evaluateName));
1562
+ let newResult = await this.rokuAdapter.getVariable(evalArgs.expression, frameId);
1563
+ this.mergeEvaluateContainers(result, newResult);
1564
+ }
1565
+ catch (error) {
1566
+ logging_1.logger.error('Error getting variables', error);
1567
+ this.mergeEvaluateContainers(result, {
1568
+ name: result.name,
1569
+ evaluateName: result.evaluateName,
1570
+ children: [],
1571
+ value: `❌ Error: ${error.message}`,
1572
+ type: '',
1573
+ highLevelType: undefined,
1574
+ keyType: undefined
1575
+ });
1576
+ }
1577
+ }
1275
1578
  if (result.keyType) {
1579
+ let value = `${(_b = result.value) !== null && _b !== void 0 ? _b : result.type}`;
1580
+ let indexedVariables = result.indexedVariables;
1581
+ let namedVariables = result.namedVariables;
1582
+ if (indexedVariables === undefined || namedVariables === undefined) {
1583
+ // If either indexed or named variables are undefined, we should tell the debugger to ask for everything
1584
+ // by supplying undefined values for both
1585
+ indexedVariables = undefined;
1586
+ namedVariables = undefined;
1587
+ }
1276
1588
  // check to see if this is an dictionary or a list
1277
1589
  if (result.keyType === 'Integer') {
1278
1590
  // list type
1279
- v = new debugadapter_1.Variable(result.name, result.type, refId, result.elementCount, 0);
1280
- this.variables[refId] = v;
1591
+ v = new debugadapter_1.Variable(result.name, value, refId, indexedVariables, namedVariables);
1281
1592
  }
1282
1593
  else if (result.keyType === 'String') {
1283
1594
  // dictionary type
1284
- v = new debugadapter_1.Variable(result.name, result.type, refId, 0, result.elementCount);
1595
+ v = new debugadapter_1.Variable(result.name, value, refId, indexedVariables, namedVariables);
1285
1596
  }
1597
+ v.type = result.type;
1286
1598
  }
1287
1599
  else {
1288
1600
  let value;
1289
1601
  if (result.type === VariablesResponse_1.VariableType.Invalid) {
1290
- value = (_a = result.value) !== null && _a !== void 0 ? _a : 'Invalid';
1602
+ value = (_c = result.value) !== null && _c !== void 0 ? _c : 'Invalid';
1291
1603
  }
1292
1604
  else if (result.type === VariablesResponse_1.VariableType.Uninitialized) {
1293
1605
  value = 'Uninitialized';
@@ -1295,7 +1607,9 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1295
1607
  else {
1296
1608
  value = `${result.value}`;
1297
1609
  }
1298
- v = new debugadapter_1.Variable(result.name, value);
1610
+ // If the variable is lazy we must assign a refId to inform the system
1611
+ // to request this variable again in the future for value resolution
1612
+ v = new debugadapter_1.Variable(result.name, value, ((_d = result === null || result === void 0 ? void 0 : result.presentationHint) === null || _d === void 0 ? void 0 : _d.lazy) ? refId : 0);
1299
1613
  }
1300
1614
  this.variables[refId] = v;
1301
1615
  }
@@ -1305,45 +1619,101 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1305
1619
  }
1306
1620
  else if (result.highLevelType === 'array') {
1307
1621
  let refId = this.getEvaluateRefId(result.evaluateName, frameId);
1308
- v = new debugadapter_1.Variable(result.name, result.type, refId, (_c = (_b = result.children) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0, 0);
1622
+ v = new debugadapter_1.Variable(result.name, result.type, refId, (_f = (_e = result.children) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0, 0);
1309
1623
  this.variables[refId] = v;
1310
1624
  }
1311
1625
  else if (result.highLevelType === 'object') {
1312
- let refId = this.getEvaluateRefId(result.evaluateName, frameId);
1313
- v = new debugadapter_1.Variable(result.name, result.type, refId, 0, (_e = (_d = result.children) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0);
1626
+ let refId;
1627
+ //handle collections
1628
+ if (this.rokuAdapter.isScrapableContainObject(result.type)) {
1629
+ refId = this.getEvaluateRefId(result.evaluateName, frameId);
1630
+ }
1631
+ v = new debugadapter_1.Variable(result.name, result.type, refId, 0, (_h = (_g = result.children) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0);
1314
1632
  this.variables[refId] = v;
1315
1633
  }
1316
1634
  else if (result.highLevelType === 'function') {
1317
- v = new debugadapter_1.Variable(result.name, result.value);
1635
+ v = new debugadapter_1.Variable(result.name, `${result.value}`);
1318
1636
  }
1319
1637
  else {
1320
1638
  //all other cases, but mostly for HighLevelType.unknown
1321
- v = new debugadapter_1.Variable(result.name, result.value);
1639
+ v = new debugadapter_1.Variable(result.name, `${result.value}`);
1322
1640
  }
1323
1641
  }
1324
1642
  v.type = result.type;
1325
1643
  v.evaluateName = result.evaluateName;
1326
1644
  v.frameId = frameId;
1327
1645
  v.type = result.type;
1328
- v.presentationHint = result.presentationHint ? { kind: result.presentationHint } : undefined;
1646
+ v.presentationHint = result.presentationHint ? { kind: (_j = result.presentationHint) === null || _j === void 0 ? void 0 : _j.kind, lazy: (_k = result.presentationHint) === null || _k === void 0 ? void 0 : _k.lazy } : undefined;
1329
1647
  if (util_1.util.isTransientVariable(v.name)) {
1330
1648
  v.presentationHint = { kind: 'virtual' };
1331
1649
  }
1332
- if (result.children) {
1333
- let childVariables = [];
1334
- for (let childContainer of result.children) {
1335
- let childVar = this.getVariableFromResult(childContainer, frameId);
1336
- childVariables.push(childVar);
1650
+ if (result.children && maxDepth > 0) {
1651
+ if (!v.childVariables) {
1652
+ v.childVariables = [];
1653
+ }
1654
+ // Create a mapping of the children to their index so we can evaluate them in bulk
1655
+ let indexMappedChildren = result.children.map((child, index) => {
1656
+ var _a;
1657
+ let remapped = { child: child, index: index, evaluate: !!(child.isCustom && !((_a = child.presentationHint) === null || _a === void 0 ? void 0 : _a.lazy) && child.evaluateNow) };
1658
+ return remapped;
1659
+ });
1660
+ if (this.enableDebugProtocol) {
1661
+ let childrenToEvaluate = indexMappedChildren.filter(x => x.evaluate);
1662
+ let evaluateArgsArray = childrenToEvaluate.map(x => {
1663
+ return { expression: x.child.evaluateName, frameId: frameId };
1664
+ });
1665
+ let variablePathArray = childrenToEvaluate.map(x => {
1666
+ return util_1.util.getVariablePath(x.child.evaluateName);
1667
+ });
1668
+ try {
1669
+ let bulkEvaluations = await this.bulkEvaluateExpressionToTempVar(frameId, evaluateArgsArray, variablePathArray);
1670
+ if (bulkEvaluations.bulkVarName) {
1671
+ let newResults = await this.rokuAdapter.getVariable(bulkEvaluations.bulkVarName, frameId);
1672
+ childrenToEvaluate.map((mappedChild, index) => {
1673
+ let newResult = newResults.children[index];
1674
+ this.mergeEvaluateContainers(mappedChild.child, newResult);
1675
+ mappedChild.child.evaluateNow = false;
1676
+ return mappedChild;
1677
+ });
1678
+ }
1679
+ }
1680
+ catch (error) {
1681
+ this.logger.error('Error getting bulk variables, will fall back to var by var lookups', error);
1682
+ }
1337
1683
  }
1338
- v.childVariables = childVariables;
1684
+ // If bulk evaluations failed, there is fall back logic in `getVariableFromResult` to do individual evaluations
1685
+ v.childVariables = await Promise.all(indexMappedChildren.map(async (mappedChild) => {
1686
+ return this.getVariableFromResult(mappedChild.child, frameId, maxDepth - 1);
1687
+ }));
1688
+ }
1689
+ else {
1690
+ v.childVariables = [];
1339
1691
  }
1340
1692
  // if the var is an array and debugProtocol is enabled, include the array size
1341
1693
  if (this.enableDebugProtocol && v.type === VariablesResponse_1.VariableType.Array) {
1342
- v.value = `${v.type}(${result.elementCount})`;
1694
+ if (isNaN(result.indexedVariables)) {
1695
+ v.value = v.type;
1696
+ }
1697
+ else {
1698
+ v.value = `${v.type}(${result.indexedVariables})`;
1699
+ }
1343
1700
  }
1344
1701
  }
1345
1702
  return v;
1346
1703
  }
1704
+ /**
1705
+ * Helper function to merge the results of an evaluate call into an existing EvaluateContainer
1706
+ * Used primarily for custom variables
1707
+ */
1708
+ mergeEvaluateContainers(original, updated) {
1709
+ original.children = updated.children;
1710
+ original.value = updated.value;
1711
+ original.type = updated.type;
1712
+ original.highLevelType = updated.highLevelType;
1713
+ original.keyType = updated.keyType;
1714
+ original.indexedVariables = updated.indexedVariables;
1715
+ original.namedVariables = updated.namedVariables;
1716
+ }
1347
1717
  getEvaluateRefId(expression, frameId) {
1348
1718
  let evaluateRefId = `${expression}-${frameId}`;
1349
1719
  if (!this.evaluateRefIdLookup[evaluateRefId]) {
@@ -1392,13 +1762,19 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1392
1762
  return this.shutdownPromise;
1393
1763
  }
1394
1764
  async _shutdown(errorMessage, modal = false) {
1395
- var _a, _b, _c, _d, _e, _f, _g;
1765
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1396
1766
  try {
1397
1767
  (_a = this.componentLibraryServer) === null || _a === void 0 ? void 0 : _a.stop();
1398
1768
  void ((_c = (_b = this.rendezvousTracker) === null || _b === void 0 ? void 0 : _b.destroy) === null || _c === void 0 ? void 0 : _c.call(_b));
1769
+ try {
1770
+ (_e = (_d = this.sourceMapManager) === null || _d === void 0 ? void 0 : _d.destroy) === null || _e === void 0 ? void 0 : _e.call(_d);
1771
+ }
1772
+ catch (e) {
1773
+ this.logger.error(e);
1774
+ }
1399
1775
  //if configured, delete the staging directory
1400
1776
  if (!this.launchConfiguration.retainStagingFolder) {
1401
- const stagingDirs = (_e = (_d = this.projectManager) === null || _d === void 0 ? void 0 : _d.getStagingDirs()) !== null && _e !== void 0 ? _e : [];
1777
+ const stagingDirs = (_g = (_f = this.projectManager) === null || _f === void 0 ? void 0 : _f.getStagingDirs()) !== null && _g !== void 0 ? _g : [];
1402
1778
  this.logger.info('deleting staging folders', stagingDirs);
1403
1779
  for (let stagingDir of stagingDirs) {
1404
1780
  try {
@@ -1416,7 +1792,7 @@ class BrightScriptDebugSession extends debugadapter_1.DebugSession {
1416
1792
  this.showPopupMessage(errorMessage, 'error', modal);
1417
1793
  }
1418
1794
  this.logger.log('Destroy rokuAdapter');
1419
- await ((_g = (_f = this.rokuAdapter) === null || _f === void 0 ? void 0 : _f.destroy) === null || _g === void 0 ? void 0 : _g.call(_f));
1795
+ await ((_j = (_h = this.rokuAdapter) === null || _h === void 0 ? void 0 : _h.destroy) === null || _j === void 0 ? void 0 : _j.call(_h));
1420
1796
  //press the home button to return to the home screen
1421
1797
  try {
1422
1798
  this.logger.log('Press home button');