roku-debug 0.8.4 → 0.9.0

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 (84) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/ChanperfTracker.js +8 -10
  3. package/dist/ChanperfTracker.js.map +1 -1
  4. package/dist/CompileErrorProcessor.d.ts +2 -1
  5. package/dist/CompileErrorProcessor.js +7 -5
  6. package/dist/CompileErrorProcessor.js.map +1 -1
  7. package/dist/ComponentLibraryServer.js +1 -0
  8. package/dist/ComponentLibraryServer.js.map +1 -1
  9. package/dist/FileUtils.d.ts +2 -2
  10. package/dist/FileUtils.js +4 -4
  11. package/dist/FileUtils.js.map +1 -1
  12. package/dist/LaunchConfiguration.d.ts +5 -0
  13. package/dist/PrintedObjectParser.js +1 -0
  14. package/dist/PrintedObjectParser.js.map +1 -1
  15. package/dist/RendezvousTracker.js +6 -8
  16. package/dist/RendezvousTracker.js.map +1 -1
  17. package/dist/SceneGraphDebugCommandController.d.ts +1 -0
  18. package/dist/SceneGraphDebugCommandController.js +14 -7
  19. package/dist/SceneGraphDebugCommandController.js.map +1 -1
  20. package/dist/adapters/DebugProtocolAdapter.d.ts +7 -15
  21. package/dist/adapters/DebugProtocolAdapter.js +32 -30
  22. package/dist/adapters/DebugProtocolAdapter.js.map +1 -1
  23. package/dist/adapters/TelnetAdapter.d.ts +11 -58
  24. package/dist/adapters/TelnetAdapter.js +262 -382
  25. package/dist/adapters/TelnetAdapter.js.map +1 -1
  26. package/dist/adapters/TelnetRequestPipeline.d.ts +51 -0
  27. package/dist/adapters/TelnetRequestPipeline.js +229 -0
  28. package/dist/adapters/TelnetRequestPipeline.js.map +1 -0
  29. package/dist/debugProtocol/Constants.js +1 -0
  30. package/dist/debugProtocol/Constants.js.map +1 -1
  31. package/dist/debugProtocol/Debugger.d.ts +9 -8
  32. package/dist/debugProtocol/Debugger.js +33 -28
  33. package/dist/debugProtocol/Debugger.js.map +1 -1
  34. package/dist/debugProtocol/responses/ConnectIOPortResponse.js +1 -0
  35. package/dist/debugProtocol/responses/ConnectIOPortResponse.js.map +1 -1
  36. package/dist/debugProtocol/responses/HandshakeResponse.js +1 -0
  37. package/dist/debugProtocol/responses/HandshakeResponse.js.map +1 -1
  38. package/dist/debugProtocol/responses/Response.js +1 -0
  39. package/dist/debugProtocol/responses/Response.js.map +1 -1
  40. package/dist/debugProtocol/responses/StackTraceResponse.js +1 -0
  41. package/dist/debugProtocol/responses/StackTraceResponse.js.map +1 -1
  42. package/dist/debugProtocol/responses/ThreadsResponse.js +1 -0
  43. package/dist/debugProtocol/responses/ThreadsResponse.js.map +1 -1
  44. package/dist/debugProtocol/responses/UndefinedResponse.js +1 -0
  45. package/dist/debugProtocol/responses/UndefinedResponse.js.map +1 -1
  46. package/dist/debugProtocol/responses/UpdateThreadsResponse.js +1 -0
  47. package/dist/debugProtocol/responses/UpdateThreadsResponse.js.map +1 -1
  48. package/dist/debugProtocol/responses/VariableResponse.d.ts +1 -1
  49. package/dist/debugProtocol/responses/VariableResponse.js +1 -0
  50. package/dist/debugProtocol/responses/VariableResponse.js.map +1 -1
  51. package/dist/debugProtocol/responses/index.js +18 -11
  52. package/dist/debugProtocol/responses/index.js.map +1 -1
  53. package/dist/debugSession/BrightScriptDebugSession.d.ts +12 -9
  54. package/dist/debugSession/BrightScriptDebugSession.js +114 -92
  55. package/dist/debugSession/BrightScriptDebugSession.js.map +1 -1
  56. package/dist/debugSession/Events.js +1 -0
  57. package/dist/debugSession/Events.js.map +1 -1
  58. package/dist/index.js +26 -18
  59. package/dist/index.js.map +1 -1
  60. package/dist/interfaces.d.ts +11 -0
  61. package/dist/interfaces.js +16 -0
  62. package/dist/interfaces.js.map +1 -0
  63. package/dist/logging.d.ts +11 -0
  64. package/dist/logging.js +14 -0
  65. package/dist/logging.js.map +1 -0
  66. package/dist/managers/BreakpointManager.js +4 -4
  67. package/dist/managers/BreakpointManager.js.map +1 -1
  68. package/dist/managers/FileManager.d.ts +2 -1
  69. package/dist/managers/FileManager.js +9 -6
  70. package/dist/managers/FileManager.js.map +1 -1
  71. package/dist/managers/LocationManager.js +8 -7
  72. package/dist/managers/LocationManager.js.map +1 -1
  73. package/dist/managers/ProjectManager.d.ts +2 -0
  74. package/dist/managers/ProjectManager.js +14 -11
  75. package/dist/managers/ProjectManager.js.map +1 -1
  76. package/dist/managers/SourceMapManager.d.ts +1 -0
  77. package/dist/managers/SourceMapManager.js +9 -8
  78. package/dist/managers/SourceMapManager.js.map +1 -1
  79. package/dist/util.d.ts +50 -14
  80. package/dist/util.js +136 -75
  81. package/dist/util.js.map +1 -1
  82. package/package.json +52 -40
  83. package/roku-debug-0.9.0.tgz +0 -0
  84. package/roku-debug-0.8.4.tgz +0 -0
@@ -1,16 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const eol = require("eol");
4
- const EventEmitter = require("events");
3
+ exports.PrimativeType = exports.KeyType = exports.EventName = exports.TelnetAdapter = void 0;
5
4
  const natural_orderby_1 = require("natural-orderby");
6
- const net = require("net");
5
+ const EventEmitter = require("eventemitter3");
6
+ const net_1 = require("net");
7
7
  const rokuDeploy = require("roku-deploy");
8
- const BrightScriptDebugSession_1 = require("../debugSession/BrightScriptDebugSession");
9
8
  const PrintedObjectParser_1 = require("../PrintedObjectParser");
10
9
  const CompileErrorProcessor_1 = require("../CompileErrorProcessor");
11
10
  const RendezvousTracker_1 = require("../RendezvousTracker");
12
11
  const ChanperfTracker_1 = require("../ChanperfTracker");
13
12
  const util_1 = require("../util");
13
+ const logging_1 = require("../logging");
14
+ const interfaces_1 = require("../interfaces");
15
+ const TelnetRequestPipeline_1 = require("./TelnetRequestPipeline");
14
16
  /**
15
17
  * A class that connects to a Roku device over telnet debugger port and provides a standardized way of interacting with it.
16
18
  */
@@ -18,6 +20,7 @@ class TelnetAdapter {
18
20
  constructor(host, enableDebuggerAutoRecovery = false) {
19
21
  this.host = host;
20
22
  this.enableDebuggerAutoRecovery = enableDebuggerAutoRecovery;
23
+ this.logger = logging_1.logger.createLogger(`[${TelnetAdapter.name}]`);
21
24
  this.isNextBreakpointSkipped = false;
22
25
  this.cache = {};
23
26
  this.supportsMultipleRuns = true;
@@ -72,6 +75,7 @@ class TelnetAdapter {
72
75
  }, 0);
73
76
  }
74
77
  async activate() {
78
+ this.logger.log('Activate TelnetAdapter');
75
79
  this.isActivated = true;
76
80
  await this.handleStartupIfReady();
77
81
  }
@@ -79,14 +83,17 @@ class TelnetAdapter {
79
83
  await this.compileErrorProcessor.sendErrors();
80
84
  }
81
85
  async handleStartupIfReady() {
86
+ var _a;
82
87
  if (this.isActivated && this.isAppRunning) {
88
+ this.logger.log('Handling startup');
83
89
  this.emit('start');
84
90
  //if we are already sitting at a debugger prompt, we need to emit the first suspend event.
85
91
  //If not, then there are probably still messages being received, so let the normal handler
86
92
  //emit the suspend event when it's ready
87
93
  if (this.isAtDebuggerPrompt === true) {
94
+ this.logger.log(`At debug prompt, so trigger the 'suspend' event`);
88
95
  let threads = await this.getThreads();
89
- this.emit('suspend', threads[0].threadId);
96
+ this.emit('suspend', (_a = threads[0]) === null || _a === void 0 ? void 0 : _a.threadId);
90
97
  }
91
98
  }
92
99
  }
@@ -97,73 +104,79 @@ class TelnetAdapter {
97
104
  * @param maxWaitMilliseconds
98
105
  */
99
106
  settle(client, name, maxWaitMilliseconds = 400) {
107
+ const startTime = new Date();
108
+ this.logger.log('Waiting for telnet client to settle');
100
109
  return new Promise((resolve) => {
101
110
  let callCount = -1;
102
- function handler() {
111
+ const handler = () => {
103
112
  callCount++;
104
113
  let myCallCount = callCount;
105
114
  setTimeout(() => {
106
115
  //if no other calls have been made since the timeout started, then the listener has settled
107
116
  if (myCallCount === callCount) {
108
117
  client.removeListener(name, handler);
118
+ this.logger.log(`Telnet client has settled after ${new Date().getTime() - startTime.getTime()} milliseconds`);
109
119
  resolve(callCount);
110
120
  }
111
121
  }, maxWaitMilliseconds);
112
- }
122
+ };
113
123
  client.addListener(name, handler);
114
124
  //call the handler immediately so we have a timeout
115
125
  handler();
116
126
  });
117
127
  }
118
128
  processBreakpoints(text) {
119
- let newLines = eol.split(text);
129
+ let newLines = text.split(/\r?\n/g);
120
130
  for (const line of newLines) {
121
- console.log('Running processing line; ', line);
131
+ //Running processing line
122
132
  if (this.debugStartRegex.exec(line)) {
123
- console.log('start MicroDebugger block');
133
+ //start MicroDebugger block
124
134
  this.isInMicroDebugger = true;
125
135
  this.isNextBreakpointSkipped = false;
126
136
  }
127
137
  else if (this.isInMicroDebugger && this.debugEndRegex.exec(line)) {
128
- console.log('ended MicroDebugger block');
138
+ //ended MicroDebugger block
129
139
  this.isInMicroDebugger = false;
130
140
  }
131
141
  else if (this.isInMicroDebugger) {
132
142
  if (this.enableDebuggerAutoRecovery && line.startsWith('Break in ')) {
133
- console.log('this block is a break: skipping it');
143
+ //this block is a break: skipping it
134
144
  this.isNextBreakpointSkipped = true;
135
145
  }
136
146
  }
137
147
  }
138
- return text;
139
148
  }
140
149
  /**
141
150
  * Connect to the telnet session. This should be called before the channel is launched.
142
151
  */
143
152
  async connect() {
144
- let deferred = BrightScriptDebugSession_1.defer();
153
+ this.logger.log('Establishing telnet connection');
154
+ let deferred = (0, util_1.defer)();
145
155
  this.isInMicroDebugger = false;
146
156
  this.isNextBreakpointSkipped = false;
147
157
  try {
158
+ this.logger.log('Pressing home button');
148
159
  //force roku to return to home screen. This gives the roku adapter some security in knowing new messages won't be appearing during initialization
149
160
  await rokuDeploy.pressHomeButton(this.host);
150
- let client = new net.Socket();
151
- client.connect(8085, this.host, () => {
152
- console.log(`+++++++++++ CONNECTED TO DEVICE ${this.host} +++++++++++`);
153
- this.connected = true;
154
- this.emit('connected', this.connected);
155
- });
161
+ let client = new net_1.Socket();
156
162
  //listen for the close event
157
163
  client.addListener('close', (err, data) => {
158
164
  this.emit('close');
159
165
  });
160
166
  //if the connection fails, reject the connect promise
161
167
  client.addListener('error', (err) => {
162
- deferred.reject(new Error(`Error with connection to: ${this.host} \n\n ${err.message}`));
168
+ deferred.reject(new Error(`Error with connection to: ${this.host} \n\n ${err.message} `));
163
169
  });
164
- await this.settle(client, 'data');
170
+ const settlePromise = this.settle(client, 'data');
171
+ client.connect(8085, this.host, () => {
172
+ this.logger.log(`Telnet connection established to ${this.host}`);
173
+ this.connected = true;
174
+ this.emit('connected', this.connected);
175
+ });
176
+ await settlePromise;
165
177
  //hook up the pipeline to the socket
166
- this.requestPipeline = new RequestPipeline(client);
178
+ this.requestPipeline = new TelnetRequestPipeline_1.TelnetRequestPipeline(client);
179
+ this.requestPipeline.connect();
167
180
  //forward all raw console output
168
181
  this.requestPipeline.on('console-output', (output) => {
169
182
  this.processBreakpoints(output);
@@ -189,13 +202,14 @@ class TelnetAdapter {
189
202
  }
190
203
  // short circuit after the output has been sent as console output
191
204
  if (hasRuntimeError) {
192
- console.debug('hasRuntimeError!!');
205
+ this.logger.log('Detected runtime error in output', { responseText });
193
206
  this.isAtDebuggerPrompt = true;
194
207
  return;
195
208
  }
196
- this.processUnhandledLines(responseText);
209
+ this.compileErrorProcessor.processUnhandledLines(responseText);
197
210
  let match;
198
211
  if (this.isAtCannotContinue(responseText)) {
212
+ this.logger.log('is at cannot continue');
199
213
  this.isAtDebuggerPrompt = true;
200
214
  return;
201
215
  }
@@ -204,6 +218,7 @@ class TelnetAdapter {
204
218
  // eslint-disable-next-line no-cond-assign
205
219
  if (match = /\[scrpt.ctx.run.enter\]/i.exec(responseText.trim())) {
206
220
  this.isAppRunning = true;
221
+ this.logger.log('Running beacon detected', { responseText });
207
222
  void this.handleStartupIfReady();
208
223
  }
209
224
  //watch for the end of the program
@@ -212,26 +227,29 @@ class TelnetAdapter {
212
227
  this.beginAppExit();
213
228
  }
214
229
  //watch for debugger prompt output
215
- // eslint-disable-next-line no-cond-assign
216
- if (match = /Brightscript\s*Debugger>\s*$/i.exec(responseText.trim())) {
230
+ if (util_1.util.endsWithDebuggerPrompt(responseText)) {
231
+ this.logger.log('Debugger prompt detected in', { responseText });
217
232
  //if we are activated AND this is the first time seeing the debugger prompt since a continue/step action
218
233
  if (this.isNextBreakpointSkipped) {
219
- console.log('this breakpoint is flagged to be skipped');
234
+ this.logger.log('This debugger is flagged to be skipped');
220
235
  this.isInMicroDebugger = false;
221
236
  this.isNextBreakpointSkipped = false;
222
- void this.requestPipeline.executeCommand('c', false, false, false);
237
+ void this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
223
238
  }
224
239
  else {
225
240
  if (this.isActivated && this.isAtDebuggerPrompt === false) {
226
241
  this.isAtDebuggerPrompt = true;
242
+ this.logger.log('Sending the "suspend" event to the client');
227
243
  this.emit('suspend');
228
244
  }
229
245
  else {
246
+ this.logger.log('Skipping "suspend" event because we are already suspended');
230
247
  this.isAtDebuggerPrompt = true;
231
248
  }
232
249
  }
233
250
  }
234
251
  else {
252
+ this.logger.debug('responseText does not end with debugger prompt. isAtDebuggerPrompt = false', { responseText });
235
253
  this.isAtDebuggerPrompt = false;
236
254
  }
237
255
  }
@@ -245,7 +263,9 @@ class TelnetAdapter {
245
263
  return deferred.promise;
246
264
  }
247
265
  beginAppExit() {
266
+ this.logger.log('Beginning app exit');
248
267
  this.compileErrorProcessor.compileErrorTimer = setTimeout(() => {
268
+ this.logger.info('emitting app-exit');
249
269
  this.isAppRunning = false;
250
270
  this.emit('app-exit');
251
271
  }, 200);
@@ -286,43 +306,46 @@ class TelnetAdapter {
286
306
  return false;
287
307
  }
288
308
  }
289
- processUnhandledLines(responseText) {
290
- this.compileErrorProcessor.processUnhandledLines(responseText);
291
- }
292
309
  /**
293
310
  * Send command to step over
294
311
  */
295
312
  stepOver() {
313
+ this.logger.log('stepOver');
296
314
  this.clearCache();
297
- return this.requestPipeline.executeCommand('over', false);
315
+ return this.requestPipeline.executeCommand('over', { waitForPrompt: false, insertAtFront: true });
298
316
  }
299
317
  stepInto() {
318
+ this.logger.log('stepInto');
300
319
  this.clearCache();
301
- return this.requestPipeline.executeCommand('step', false);
320
+ return this.requestPipeline.executeCommand('step', { waitForPrompt: false, insertAtFront: true });
302
321
  }
303
322
  stepOut() {
323
+ this.logger.log('stepOut');
304
324
  this.clearCache();
305
- return this.requestPipeline.executeCommand('out', false);
325
+ return this.requestPipeline.executeCommand('out', { waitForPrompt: false, insertAtFront: true });
306
326
  }
307
327
  /**
308
328
  * Tell the brightscript program to continue (i.e. resume program)
309
329
  */
310
330
  continue() {
331
+ this.logger.log('continue');
311
332
  this.clearCache();
312
- return this.requestPipeline.executeCommand('c', false);
333
+ return this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
313
334
  }
314
335
  /**
315
336
  * Tell the brightscript program to pause (fall into debug mode)
316
337
  */
317
338
  pause() {
339
+ this.logger.log('pause');
318
340
  this.clearCache();
319
- //send the kill signal, which breaks into debugger mode
320
- return this.requestPipeline.executeCommand('\x03;', false, true);
341
+ //send the kill signal, which breaks into debugger mode. This gets written immediately, regardless of debugger prompt status.
342
+ this.requestPipeline.write('\x03;');
321
343
  }
322
344
  /**
323
345
  * Clears the state, which means that everything will be retrieved fresh next time it is requested
324
346
  */
325
347
  clearCache() {
348
+ this.logger.info('Clearing TelnetAdapter cache');
326
349
  this.cache = {};
327
350
  this.isAtDebuggerPrompt = false;
328
351
  }
@@ -331,24 +354,26 @@ class TelnetAdapter {
331
354
  * @param command the command to execute. If the command does not start with `print` the command will be prefixed with `print ` because
332
355
  */
333
356
  async evaluate(command) {
357
+ this.logger.log('evaluate ', { command });
334
358
  if (!this.isAtDebuggerPrompt) {
335
359
  throw new Error('Cannot run evaluate: debugger is not paused');
336
360
  }
337
361
  //clear the cache (we don't know what command the user entered)
338
362
  this.clearCache();
339
363
  //don't wait for the output...we don't know what command the user entered
340
- let responseText = await this.requestPipeline.executeCommand(command, true);
364
+ let responseText = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
341
365
  //we know that if we got a response, we are back at a debugger prompt
342
366
  this.isAtDebuggerPrompt = true;
343
367
  return responseText;
344
368
  }
345
369
  async getStackTrace() {
370
+ this.logger.log(TelnetAdapter.prototype.getStackTrace.name);
346
371
  if (!this.isAtDebuggerPrompt) {
347
372
  throw new Error('Cannot get stack trace: debugger is not paused');
348
373
  }
349
374
  return this.resolve('stackTrace', async () => {
350
375
  //perform a request to load the stack trace
351
- let responseText = await this.requestPipeline.executeCommand('bt', true);
376
+ let responseText = (await this.requestPipeline.executeCommand('bt', { waitForPrompt: true })).trim();
352
377
  let regexp = /#(\d+)\s+(?:function|sub)\s+([\$\w\d]+).*\s+file\/line:\s+(.*)\((\d+)\)/ig;
353
378
  let matches;
354
379
  let frames = [];
@@ -375,16 +400,6 @@ class TelnetAdapter {
375
400
  return frames;
376
401
  });
377
402
  }
378
- /**
379
- * Runs a regex to get the content between telnet commands
380
- * @param value
381
- */
382
- getExpressionDetails(value) {
383
- const match = /(.*?)\r?\nBrightscript Debugger>\s*/is.exec(value);
384
- if (match) {
385
- return match[1];
386
- }
387
- }
388
403
  /**
389
404
  * Runs a regex to check if the target is an object and get the type if it is
390
405
  * @param value
@@ -410,14 +425,15 @@ class TelnetAdapter {
410
425
  * @param scope
411
426
  */
412
427
  async getScopeVariables(scope) {
428
+ this.logger.log('getScopeVariables', { scope });
413
429
  if (!this.isAtDebuggerPrompt) {
414
430
  throw new Error('Cannot resolve variable: debugger is not paused');
415
431
  }
416
432
  return this.resolve(`Scope Variables`, async () => {
417
433
  let data;
418
434
  let vars = [];
419
- data = await this.requestPipeline.executeCommand(`var`, true);
420
- let splitData = data.split('\n');
435
+ data = await this.requestPipeline.executeCommand(`var`, { waitForPrompt: true });
436
+ let splitData = data.trim().split('\n');
421
437
  for (const line of splitData) {
422
438
  let match;
423
439
  if (!line.includes('Brightscript Debugger') && (match = this.getFirstWord(line))) {
@@ -436,97 +452,141 @@ class TelnetAdapter {
436
452
  * @param expression
437
453
  */
438
454
  async getVariable(expression) {
455
+ const logger = this.logger.createLogger('[getVariable]');
456
+ logger.info('begin', { expression });
439
457
  if (!this.isAtDebuggerPrompt) {
440
458
  throw new Error('Cannot resolve variable: debugger is not paused');
441
459
  }
442
- return this.resolve(`variable: ${expression}`, async () => {
443
- let expressionType = await this.getVariableType(expression);
444
- let lowerExpressionType = expressionType ? expressionType.toLowerCase() : null;
445
- let data;
446
- //if the expression type is a string, we need to wrap the expression in quotes BEFORE we run the print so we can accurately capture the full string value
447
- if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
448
- data = await this.requestPipeline.executeCommand(`print "--string-wrap--" + ${expression} + "--string-wrap--"`, true);
449
- //write a for loop to print every value from the array. This gets around the `...` after the 100th item issue in the roku print call
450
- }
451
- else if (['roarray', 'rolist', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
452
- data = await this.requestPipeline.executeCommand(`for each vscodeLoopItem in ${expression} : print "vscode_is_string:"; (invalid <> GetInterface(vscodeLoopItem, "ifString")); vscodeLoopItem : end for`, true);
453
- }
454
- else if (['roassociativearray', 'rosgnode'].includes(lowerExpressionType)) {
455
- data = await this.requestPipeline.executeCommand(`for each vscodeLoopKey in ${expression}.keys() : print "vscode_key_start:" + vscodeLoopKey + ":vscode_key_stop " + "vscode_is_string:"; (invalid <> GetInterface(${expression}[vscodeLoopKey], "ifString")); ${expression}[vscodeLoopKey] : end for`, true);
456
- }
457
- else {
458
- data = await this.requestPipeline.executeCommand(`print ${expression}`, true);
459
- }
460
- let match = this.getExpressionDetails(data);
461
- if (match !== undefined) {
462
- let value = match;
463
- if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
464
- value = value.trim().replace(/--string-wrap--/g, '');
465
- //add an escape character in front of any existing quotes
466
- value = value.replace(/"/g, '\\"');
467
- //wrap the string value with literal quote marks
468
- value = '"' + value + '"';
469
- }
470
- let highLevelType = this.getHighLevelType(expressionType);
471
- let children;
472
- if (highLevelType === HighLevelType.array || ['roassociativearray', 'rosgnode', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
473
- //the print statment will always have 1 trailing newline, so remove that.
474
- value = util_1.util.removeTrailingNewline(value);
475
- //the array/associative array print is a loop of every value, so handle that
476
- children = this.getForLoopPrintedChildren(expression, value);
477
- }
478
- else if (highLevelType === HighLevelType.object) {
479
- children = this.getObjectChildren(expression, value.trim());
480
- }
481
- //add a computed `[[children]]` property to allow expansion of node children
482
- if (lowerExpressionType === 'rosgnode') {
483
- let nodeChildren = {
484
- name: '[[children]]',
485
- type: 'roArray',
486
- highLevelType: 'array',
487
- evaluateName: `${expression}.getChildren(-1,0)`,
488
- children: []
489
- };
490
- children.push(nodeChildren);
491
- }
492
- //xml elements won't display on their own, so we need to create some sub elements
493
- if (lowerExpressionType === 'roxmlelement') {
494
- //add a computed `[[children]]` property to allow expansion of node children
495
- children.push({
496
- name: '[[children]]',
497
- type: 'roArray',
498
- highLevelType: HighLevelType.array,
499
- evaluateName: `${expression}.GetChildNodes()`,
500
- children: []
501
- });
502
- children.push({
503
- name: '[[attributes]]',
504
- type: 'roArray',
505
- highLevelType: HighLevelType.array,
506
- evaluateName: `${expression}.GetAttributes()`,
507
- children: []
508
- });
509
- //look up the element name right now
510
- const container = await this.getVariable(`${expression}.GetName()`);
511
- container.name = '[[name]]';
512
- children.push(container);
513
- }
514
- //if this item is an array or a list, add the item count to the end of the type
515
- if (highLevelType === HighLevelType.array) {
516
- //TODO re-enable once we find how to refresh watch/variables panel, since lazy loaded arrays can't show a length
517
- //expressionType += `(${children.length})`;
518
- }
519
- let container = {
520
- name: expression,
521
- evaluateName: expression,
522
- type: expressionType,
523
- value: value.trim(),
524
- highLevelType: highLevelType,
525
- children: children
526
- };
527
- return container;
528
- }
529
- });
460
+ let expressionType = await this.getVariableType(expression);
461
+ let lowerExpressionType = expressionType ? expressionType.toLowerCase() : null;
462
+ let data;
463
+ //if the expression type is a string, we need to wrap the expression in quotes BEFORE we run the print so we can accurately capture the full string value
464
+ if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
465
+ data = await this.requestPipeline.executeCommand(`print "--string-wrap--" + ${expression} + "--string-wrap--"`, { waitForPrompt: true });
466
+ //write a for loop to print every value from the array. This gets around the `...` after the 100th item issue in the roku print call
467
+ }
468
+ else if (['roarray', 'rolist', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
469
+ const command = [
470
+ `for each vscodeLoopItem in ${expression} : print ` +
471
+ ` "vscode_type_start:" + type(vscodeLoopItem) + ":vscode_type_stop "`,
472
+ ` "vscode_is_string:"; (invalid <> GetInterface(vscodeLoopItem, "ifString"))`,
473
+ ` vscodeLoopItem :` +
474
+ ` end for`
475
+ ].join(';');
476
+ data = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
477
+ }
478
+ else if (['roassociativearray', 'rosgnode'].includes(lowerExpressionType)) {
479
+ const command = [
480
+ `for each vscodeLoopKey in ${expression}.keys(): print` +
481
+ ` "vscode_key_start:" + vscodeLoopKey + ":vscode_key_stop "`,
482
+ ` "vscode_type_start:" + type(${expression}[vscodeLoopKey]) + ":vscode_type_stop "`,
483
+ ` "vscode_is_string:"; (invalid <> GetInterface(${expression}[vscodeLoopKey], "ifString"))`,
484
+ ` ${expression}[vscodeLoopKey] :` +
485
+ ' end for'
486
+ ].join(';');
487
+ data = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
488
+ }
489
+ else {
490
+ data = await this.requestPipeline.executeCommand(`print ${expression}`, { waitForPrompt: true });
491
+ }
492
+ logger.info('expression details', { data });
493
+ //remove excess whitespace
494
+ data = data.trim();
495
+ if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
496
+ data = data.trim().replace(/--string-wrap--/g, '');
497
+ //add an escape character in front of any existing quotes
498
+ data = data.replace(/"/g, '\\"');
499
+ //wrap the string value with literal quote marks
500
+ data = '"' + data + '"';
501
+ }
502
+ let highLevelType = this.getHighLevelType(expressionType);
503
+ let children;
504
+ if (highLevelType === interfaces_1.HighLevelType.array || ['roassociativearray', 'rosgnode', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
505
+ //the print statment will always have 1 trailing newline, so remove that.
506
+ data = util_1.util.removeTrailingNewline(data);
507
+ //the array/associative array print is a loop of every value, so handle that
508
+ children = this.getForLoopPrintedChildren(expression, data);
509
+ children.push({
510
+ name: '[[count]]',
511
+ value: children.length.toString(),
512
+ type: 'integer',
513
+ highLevelType: interfaces_1.HighLevelType.primative,
514
+ evaluateName: children.length.toString(),
515
+ presentationHint: 'virtual',
516
+ keyType: KeyType.legacy,
517
+ children: undefined
518
+ });
519
+ }
520
+ else if (highLevelType === interfaces_1.HighLevelType.object) {
521
+ children = this.getObjectChildren(expression, data.trim());
522
+ }
523
+ else if (highLevelType === interfaces_1.HighLevelType.unknown) {
524
+ logger.warn('there was an issue evaluating this variable', { expression });
525
+ data = '<UNKNOWN>';
526
+ }
527
+ if (['rostring', 'roint', 'rointeger', 'rolonginteger', 'rofloat', 'rodouble', 'roboolean', 'rointrinsicdouble'].includes(lowerExpressionType)) {
528
+ return {
529
+ name: expression,
530
+ value: util_1.util.removeTrailingNewline(data),
531
+ type: expressionType,
532
+ highLevelType: interfaces_1.HighLevelType.primative,
533
+ evaluateName: expression,
534
+ children: []
535
+ };
536
+ }
537
+ //add a computed `[[children]]` property to allow expansion of node children
538
+ if (lowerExpressionType === 'rosgnode') {
539
+ let nodeChildren = {
540
+ name: '[[children]]',
541
+ type: 'roArray',
542
+ highLevelType: 'array',
543
+ presentationHint: 'virtual',
544
+ evaluateName: `${expression}.getChildren(-1, 0)`,
545
+ children: []
546
+ };
547
+ children.push(nodeChildren);
548
+ }
549
+ //xml elements won't display on their own, so we need to create some sub elements
550
+ if (lowerExpressionType === 'roxmlelement') {
551
+ children.push({
552
+ //look up the name of the xml element
553
+ ...await this.getVariable(`${expression}.GetName()`),
554
+ name: '[[name]]',
555
+ presentationHint: 'virtual'
556
+ });
557
+ children.push({
558
+ name: '[[attributes]]',
559
+ type: 'roAssociativeArray',
560
+ highLevelType: interfaces_1.HighLevelType.array,
561
+ evaluateName: `${expression}.GetAttributes()`,
562
+ presentationHint: 'virtual',
563
+ children: []
564
+ });
565
+ //add a computed `[[children]]` property to allow expansion of child elements
566
+ children.push({
567
+ name: '[[children]]',
568
+ type: 'roArray',
569
+ highLevelType: interfaces_1.HighLevelType.array,
570
+ evaluateName: `${expression}.GetChildNodes()`,
571
+ presentationHint: 'virtual',
572
+ children: []
573
+ });
574
+ }
575
+ //if this item is an array or a list, add the item count to the end of the type
576
+ if (highLevelType === interfaces_1.HighLevelType.array) {
577
+ //TODO re-enable once we find how to refresh watch/variables panel, since lazy loaded arrays can't show a length
578
+ //expressionType += `(${children.length})`;
579
+ }
580
+ let container = {
581
+ name: expression,
582
+ evaluateName: expression,
583
+ type: expressionType,
584
+ value: data.trim(),
585
+ highLevelType: highLevelType,
586
+ children: children
587
+ };
588
+ logger.info('end', { container });
589
+ return container;
530
590
  }
531
591
  /**
532
592
  * In order to get around the `...` issue in printed arrays, `getVariable` now prints every value from an array or associative array in a for loop.
@@ -534,7 +594,7 @@ class TelnetAdapter {
534
594
  */
535
595
  getForLoopPrintedChildren(expression, data) {
536
596
  let children = [];
537
- let lines = eol.split(data);
597
+ let lines = data.split(/\r?\n/g);
538
598
  //if there are no lines, this is an empty object/array
539
599
  if (lines.length === 1 && lines[0].trim() === '') {
540
600
  return children;
@@ -558,6 +618,17 @@ class TelnetAdapter {
558
618
  child.name = children.length.toString();
559
619
  child.evaluateName = `${expression}[${children.length}]`;
560
620
  }
621
+ //get the object type
622
+ let typeStartWrapper = 'vscode_type_start:';
623
+ let typeStopWrapper = ':vscode_type_stop ';
624
+ let type;
625
+ const typeStartIndex = line.indexOf(typeStartWrapper);
626
+ //if the type is present, extract it
627
+ if (typeStartIndex > -1) {
628
+ type = line.substring(typeStartIndex + typeStartWrapper.length, line.indexOf(typeStopWrapper));
629
+ //throw out the type chunk
630
+ line = line.substring(line.indexOf(typeStopWrapper) + typeStopWrapper.length);
631
+ }
561
632
  if (line.includes('vscode_is_string:true')) {
562
633
  line = line.replace('vscode_is_string:true', '');
563
634
  //support multi-line strings
@@ -590,22 +661,22 @@ class TelnetAdapter {
590
661
  let collectionEnd;
591
662
  if (line.includes('<Component: roList>')) {
592
663
  collectionEnd = ')';
593
- child.highLevelType = HighLevelType.array;
664
+ child.highLevelType = interfaces_1.HighLevelType.array;
594
665
  child.type = objectType;
595
666
  }
596
667
  else if (line.includes('<Component: roArray>')) {
597
668
  collectionEnd = ']';
598
- child.highLevelType = HighLevelType.array;
669
+ child.highLevelType = interfaces_1.HighLevelType.array;
599
670
  child.type = this.getObjectType(line);
600
671
  }
601
672
  else if (line.includes('<Component: roByteArray>')) {
602
673
  collectionEnd = ']';
603
- child.highLevelType = HighLevelType.array;
674
+ child.highLevelType = interfaces_1.HighLevelType.array;
604
675
  child.type = this.getObjectType(line);
605
676
  }
606
677
  else if (line.includes('<Component: roAssociativeArray>') || isRoSGNode) {
607
678
  collectionEnd = '}';
608
- child.highLevelType = HighLevelType.object;
679
+ child.highLevelType = interfaces_1.HighLevelType.object;
609
680
  child.type = this.getObjectType(line);
610
681
  }
611
682
  let collectionLineList = [line];
@@ -617,53 +688,31 @@ class TelnetAdapter {
617
688
  break;
618
689
  }
619
690
  }
620
- //if the next-to-last line of collection is `...`, then scrap the values
621
- //because we will need to run a full evaluation (later) to get around the `...` issue
622
- if (collectionLineList.length > 3 && collectionLineList[collectionLineList.length - 2].trim() === '...') {
623
- child.children = [];
624
- //get the object children
625
- }
626
- else if (child.highLevelType === HighLevelType.object) {
627
- child.children = this.getObjectChildren(child.evaluateName, collectionLineList.join('\n'));
628
- //get all of the array children right now since we have them
629
- }
630
- else {
631
- child.children = this.getArrayOrListChildren(child.evaluateName, collectionLineList.join('\n'));
632
- child.type += `(${child.children.length})`;
633
- }
634
- if (isRoSGNode) {
635
- let nodeChildrenProperty = {
636
- name: '[[children]]',
637
- type: 'roArray',
638
- highLevelType: 'array',
639
- evaluateName: `${child.evaluateName}.getChildren(-1,0)`,
640
- children: []
641
- };
642
- child.children.push(nodeChildrenProperty);
643
- }
644
- //this if block must preseec the `line.indexOf('<Component') > -1` line because roInvalid is a component too.
691
+ //we have reached the end of the collection. scrap children because they need evaluated in a separate call to compute their types
692
+ child.children = [];
693
+ //this if block must pre-seek the `line.indexOf('<Component') > -1` line because roInvalid is a component too.
645
694
  }
646
695
  else if (objectType === 'roInvalid') {
647
- child.highLevelType = HighLevelType.uninitialized;
696
+ child.highLevelType = interfaces_1.HighLevelType.uninitialized;
648
697
  child.type = 'roInvalid';
649
698
  child.value = 'roInvalid';
650
699
  child.children = undefined;
651
700
  }
652
701
  else if (line.includes('<Component:')) {
653
702
  //handle things like nodes
654
- child.highLevelType = HighLevelType.object;
703
+ child.highLevelType = interfaces_1.HighLevelType.object;
655
704
  child.type = objectType;
656
705
  }
657
706
  else {
658
707
  //is some primative type
659
- child.type = this.getPrimativeTypeFromValue(line);
708
+ child.type = type;
660
709
  child.value = line.trim();
661
- child.highLevelType = HighLevelType.primative;
710
+ child.highLevelType = interfaces_1.HighLevelType.primative;
662
711
  child.children = undefined;
663
712
  }
664
713
  children.push(child);
665
714
  }
666
- let sortedChildren = natural_orderby_1.orderBy(children, ['name']);
715
+ let sortedChildren = (0, natural_orderby_1.orderBy)(children, ['name']);
667
716
  return sortedChildren;
668
717
  }
669
718
  /**
@@ -681,7 +730,7 @@ class TelnetAdapter {
681
730
  }
682
731
  let children = [];
683
732
  //split by newline. the array contents start at index 2
684
- let lines = eol.split(data);
733
+ let lines = data.split(/\r?\n/g);
685
734
  let arrayIndex = 0;
686
735
  for (let i = 2; i < lines.length; i++) {
687
736
  let line = lines[i].trim();
@@ -704,7 +753,7 @@ class TelnetAdapter {
704
753
  else {
705
754
  child.type = this.getPrimativeTypeFromValue(line);
706
755
  child.value = line;
707
- child.highLevelType = HighLevelType.primative;
756
+ child.highLevelType = interfaces_1.HighLevelType.primative;
708
757
  }
709
758
  children.push(child);
710
759
  arrayIndex++;
@@ -733,7 +782,7 @@ class TelnetAdapter {
733
782
  try {
734
783
  let children = [];
735
784
  //split by newline. the object contents start at index 2
736
- let lines = eol.split(data);
785
+ let lines = data.split(/\r?\n/g);
737
786
  for (let i = 2; i < lines.length; i++) {
738
787
  let line = lines[i];
739
788
  let trimmedLine = line.trim();
@@ -749,7 +798,7 @@ class TelnetAdapter {
749
798
  child = {
750
799
  name: line,
751
800
  type: '<ERROR>',
752
- highLevelType: HighLevelType.uninitialized,
801
+ highLevelType: interfaces_1.HighLevelType.uninitialized,
753
802
  evaluateName: undefined,
754
803
  variablePath: [],
755
804
  elementCount: -1,
@@ -774,7 +823,7 @@ class TelnetAdapter {
774
823
  else {
775
824
  child.type = this.getPrimativeTypeFromValue(trimmedLine);
776
825
  child.value = lineParseResult.value;
777
- child.highLevelType = HighLevelType.primative;
826
+ child.highLevelType = interfaces_1.HighLevelType.primative;
778
827
  }
779
828
  }
780
829
  children.push(child);
@@ -782,7 +831,7 @@ class TelnetAdapter {
782
831
  return children;
783
832
  }
784
833
  catch (e) {
785
- throw new Error(`Unable to parse BrightScript object: ${e.message}. Data: ${data}`);
834
+ throw new Error(`Unable to parse BrightScript object: ${JSON.stringify(e.message)}. Data: ${data}`);
786
835
  }
787
836
  }
788
837
  /**
@@ -791,24 +840,24 @@ class TelnetAdapter {
791
840
  */
792
841
  getHighLevelType(expressionType) {
793
842
  if (!expressionType) {
794
- throw new Error(`Unknown expression type: ${expressionType}`);
843
+ return interfaces_1.HighLevelType.unknown;
795
844
  }
796
845
  expressionType = expressionType.toLowerCase();
797
846
  let primativeTypes = ['boolean', 'integer', 'longinteger', 'float', 'double', 'string', 'rostring', 'invalid'];
798
847
  if (primativeTypes.includes(expressionType)) {
799
- return HighLevelType.primative;
848
+ return interfaces_1.HighLevelType.primative;
800
849
  }
801
850
  else if (expressionType === 'roarray' || expressionType === 'rolist') {
802
- return HighLevelType.array;
851
+ return interfaces_1.HighLevelType.array;
803
852
  }
804
853
  else if (expressionType === 'function') {
805
- return HighLevelType.function;
854
+ return interfaces_1.HighLevelType.function;
806
855
  }
807
856
  else if (expressionType === '<uninitialized>') {
808
- return HighLevelType.uninitialized;
857
+ return interfaces_1.HighLevelType.uninitialized;
809
858
  }
810
859
  else {
811
- return HighLevelType.object;
860
+ return interfaces_1.HighLevelType.object;
812
861
  }
813
862
  }
814
863
  /**
@@ -821,17 +870,10 @@ class TelnetAdapter {
821
870
  }
822
871
  expression = `Type(${expression})`;
823
872
  return this.resolve(`${expression}`, async () => {
824
- let data = await this.requestPipeline.executeCommand(`print ${expression}`, true);
825
- let match = this.getExpressionDetails(data);
826
- if (match) {
827
- let typeValue = match;
828
- //remove whitespace
829
- typeValue = typeValue.trim();
830
- return typeValue;
831
- }
832
- else {
833
- return null;
834
- }
873
+ var _a;
874
+ let data = await this.requestPipeline.executeCommand(`print ${expression}`, { waitForPrompt: true });
875
+ //remove whitespace
876
+ return (_a = data === null || data === void 0 ? void 0 : data.trim()) !== null && _a !== void 0 ? _a : null;
835
877
  });
836
878
  }
837
879
  /**
@@ -840,22 +882,34 @@ class TelnetAdapter {
840
882
  * @param factory
841
883
  */
842
884
  resolve(key, factory) {
843
- if (this.cache[key]) {
844
- return this.cache[key];
885
+ try {
886
+ if (this.cache[key]) {
887
+ this.logger.debug(`resolve cache "${key}": already exists`);
888
+ return this.cache[key];
889
+ }
890
+ else {
891
+ this.logger.debug(`resolve cache "${key}": calling factory`);
892
+ const result = factory();
893
+ this.cache[key] = Promise.resolve(result);
894
+ return this.cache[key];
895
+ }
896
+ }
897
+ catch (e) {
898
+ return Promise.reject(e);
845
899
  }
846
- this.cache[key] = Promise.resolve(factory());
847
- return this.cache[key];
848
900
  }
849
901
  /**
850
902
  * Get a list of threads. The first thread in the list is the active thread
851
903
  */
852
904
  async getThreads() {
905
+ this.logger.log('getThreads');
853
906
  if (!this.isAtDebuggerPrompt) {
854
- throw new Error('Cannot get threads: debugger is not paused');
907
+ this.logger.log('Cannot get threads: debugger is not paused');
908
+ return [];
855
909
  }
856
910
  return this.resolve('threads', async () => {
857
- let data = await this.requestPipeline.executeCommand('threads', true);
858
- let dataString = data.toString();
911
+ let data = await this.requestPipeline.executeCommand('threads', { waitForPrompt: true });
912
+ let dataString = data.toString().trim();
859
913
  let matches = /^\s+(\d+\*)\s+(.*)\((\d+)\)\s+(.*)/gm.exec(dataString);
860
914
  let threads = [];
861
915
  if (matches) {
@@ -886,9 +940,8 @@ class TelnetAdapter {
886
940
  /**
887
941
  * Disconnect from the telnet session and unset all objects
888
942
  */
889
- async destroy() {
943
+ destroy() {
890
944
  if (this.requestPipeline) {
891
- await this.exitActiveBrightscriptDebugger();
892
945
  this.requestPipeline.destroy();
893
946
  }
894
947
  this.requestPipeline = undefined;
@@ -897,20 +950,8 @@ class TelnetAdapter {
897
950
  this.emitter.removeAllListeners();
898
951
  }
899
952
  this.emitter = undefined;
900
- }
901
- /**
902
- * Make sure any active Brightscript Debugger threads are exited
903
- */
904
- async exitActiveBrightscriptDebugger() {
905
- if (this.requestPipeline) {
906
- let commandsExecuted = 0;
907
- do {
908
- let data = await this.requestPipeline.executeCommand(`exit`, false);
909
- // This seems to work without the delay but I wonder about slower devices
910
- // await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](100);
911
- commandsExecuted++;
912
- } while (commandsExecuted < 10);
913
- }
953
+ //needs to be async to match the DebugProtocolAdapter implementation
954
+ return Promise.resolve();
914
955
  }
915
956
  // #region Rendezvous Tracker pass though functions
916
957
  /**
@@ -945,14 +986,6 @@ var EventName;
945
986
  (function (EventName) {
946
987
  EventName["suspend"] = "suspend";
947
988
  })(EventName = exports.EventName || (exports.EventName = {}));
948
- var HighLevelType;
949
- (function (HighLevelType) {
950
- HighLevelType["primative"] = "primative";
951
- HighLevelType["array"] = "array";
952
- HighLevelType["function"] = "function";
953
- HighLevelType["object"] = "object";
954
- HighLevelType["uninitialized"] = "uninitialized";
955
- })(HighLevelType = exports.HighLevelType || (exports.HighLevelType = {}));
956
989
  var KeyType;
957
990
  (function (KeyType) {
958
991
  KeyType["string"] = "String";
@@ -967,157 +1000,4 @@ var PrimativeType;
967
1000
  PrimativeType["integer"] = "Integer";
968
1001
  PrimativeType["float"] = "Float";
969
1002
  })(PrimativeType = exports.PrimativeType || (exports.PrimativeType = {}));
970
- class RequestPipeline {
971
- constructor(client) {
972
- this.client = client;
973
- this.requests = [];
974
- this.isAtDebuggerPrompt = false;
975
- this.currentRequest = undefined;
976
- this.emitter = new EventEmitter();
977
- this.debuggerLineRegex = /Brightscript\s+Debugger>\s*$/i;
978
- this.connect();
979
- }
980
- get isProcessing() {
981
- return this.currentRequest !== undefined;
982
- }
983
- get hasRequests() {
984
- return this.requests.length > 0;
985
- }
986
- on(eventName, handler) {
987
- this.emitter.on(eventName, handler);
988
- return () => {
989
- this.emitter.removeListener(eventName, handler);
990
- };
991
- }
992
- emit(eventName, data) {
993
- this.emitter.emit(eventName, data);
994
- }
995
- connect() {
996
- let allResponseText = '';
997
- let lastPartialLine = '';
998
- this.client.addListener('data', (data) => {
999
- let responseText = data.toString();
1000
- const cumulative = lastPartialLine + responseText;
1001
- if (!cumulative.endsWith('\n') && !this.checkForDebuggerPrompt(cumulative)) {
1002
- // buffer was split and was not the result of a prompt, save the partial line
1003
- lastPartialLine += responseText;
1004
- return;
1005
- }
1006
- if (lastPartialLine) {
1007
- // there was leftover lines, join the partial lines back together
1008
- responseText = lastPartialLine + responseText;
1009
- lastPartialLine = '';
1010
- }
1011
- //forward all raw console output
1012
- this.emit('console-output', responseText);
1013
- allResponseText += responseText;
1014
- let foundDebuggerPrompt = this.checkForDebuggerPrompt(allResponseText);
1015
- //if we are not processing, immediately broadcast the latest data
1016
- if (!this.isProcessing) {
1017
- this.emit('unhandled-console-output', allResponseText);
1018
- allResponseText = '';
1019
- if (foundDebuggerPrompt) {
1020
- this.isAtDebuggerPrompt = true;
1021
- if (this.hasRequests) {
1022
- // There are requests waiting to be processed
1023
- this.process();
1024
- }
1025
- }
1026
- }
1027
- else {
1028
- //if responseText produced a prompt, return the responseText
1029
- if (foundDebuggerPrompt) {
1030
- //resolve the command's promise (if it cares)
1031
- this.isAtDebuggerPrompt = true;
1032
- this.currentRequest.onComplete(allResponseText);
1033
- allResponseText = '';
1034
- this.currentRequest = undefined;
1035
- //try to run the next request
1036
- this.process();
1037
- }
1038
- }
1039
- });
1040
- }
1041
- /**
1042
- * Checks the supplied string for the debugger input prompt
1043
- * @param responseText
1044
- */
1045
- checkForDebuggerPrompt(responseText) {
1046
- let match = this.debuggerLineRegex.exec(responseText.trim());
1047
- return (match);
1048
- }
1049
- /**
1050
- * Schedule a command to be run. Resolves with the result once the command finishes
1051
- * @param commandFunction
1052
- * @param waitForPrompt - if true, the promise will wait until we find a prompt, and return all output in between. If false, the promise will immediately resolve
1053
- * @param forceExecute - if true, it is assumed the command can be run at any time and will be executed immediately
1054
- * @param silent - if true, the command will be hidden from the output
1055
- */
1056
- executeCommand(command, waitForPrompt, forceExecute = false, silent = false) {
1057
- console.debug(`Execute command (and ${waitForPrompt ? 'do' : 'do not'} wait for prompt):`, command);
1058
- return new Promise((resolve, reject) => {
1059
- let executeCommand = () => {
1060
- let commandText = `${command}\r\n`;
1061
- if (!silent) {
1062
- this.emit('console-output', command);
1063
- }
1064
- this.client.write(commandText);
1065
- if (waitForPrompt) {
1066
- // The act of executing this command means we are no longer at the debug prompt
1067
- this.isAtDebuggerPrompt = false;
1068
- }
1069
- };
1070
- let request = {
1071
- executeCommand: executeCommand,
1072
- onComplete: (data) => {
1073
- console.debug(`Command finished (${waitForPrompt ? 'after waiting for prompt' : 'did not wait for prompt'}`, command);
1074
- console.debug('Data:', data);
1075
- resolve(data);
1076
- },
1077
- waitForPrompt: waitForPrompt
1078
- };
1079
- if (!waitForPrompt) {
1080
- if (!this.isProcessing || forceExecute) {
1081
- //fire and forget the command
1082
- request.executeCommand();
1083
- //the command doesn't care about the output, resolve it immediately
1084
- request.onComplete(undefined);
1085
- }
1086
- else {
1087
- // Skip this request as the device is not ready to accept the command or it can not be run at any time
1088
- }
1089
- }
1090
- else {
1091
- this.requests.push(request);
1092
- if (this.isAtDebuggerPrompt) {
1093
- //start processing since we are already at a debug prompt (safe to call multiple times)
1094
- this.process();
1095
- }
1096
- else {
1097
- // do not run the command until the device is at a debug prompt.
1098
- // this will be detected in the data listener in the connect function
1099
- }
1100
- }
1101
- });
1102
- }
1103
- /**
1104
- * Internal request processing function
1105
- */
1106
- process() {
1107
- if (this.isProcessing || !this.hasRequests) {
1108
- return;
1109
- }
1110
- //get the oldest command
1111
- let nextRequest = this.requests.shift();
1112
- this.currentRequest = nextRequest;
1113
- //run the request. the data listener will handle launching the next request once this one has finished processing
1114
- nextRequest.executeCommand();
1115
- }
1116
- destroy() {
1117
- this.client.removeAllListeners();
1118
- this.client.destroy();
1119
- this.client = undefined;
1120
- }
1121
- }
1122
- exports.RequestPipeline = RequestPipeline;
1123
1003
  //# sourceMappingURL=TelnetAdapter.js.map