@theia/debug 1.67.0-next.59 → 1.67.0-next.86

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 (70) hide show
  1. package/lib/browser/console/debug-console-items.d.ts +13 -3
  2. package/lib/browser/console/debug-console-items.d.ts.map +1 -1
  3. package/lib/browser/console/debug-console-items.js +81 -20
  4. package/lib/browser/console/debug-console-items.js.map +1 -1
  5. package/lib/browser/console/debug-console-session.d.ts.map +1 -1
  6. package/lib/browser/console/debug-console-session.js +1 -0
  7. package/lib/browser/console/debug-console-session.js.map +1 -1
  8. package/lib/browser/debug-session-contribution.d.ts.map +1 -1
  9. package/lib/browser/debug-session-contribution.js +1 -1
  10. package/lib/browser/debug-session-contribution.js.map +1 -1
  11. package/lib/browser/debug-session-manager.d.ts +7 -0
  12. package/lib/browser/debug-session-manager.d.ts.map +1 -1
  13. package/lib/browser/debug-session-manager.js +3 -0
  14. package/lib/browser/debug-session-manager.js.map +1 -1
  15. package/lib/browser/debug-session.d.ts +7 -2
  16. package/lib/browser/debug-session.d.ts.map +1 -1
  17. package/lib/browser/debug-session.js +10 -1
  18. package/lib/browser/debug-session.js.map +1 -1
  19. package/lib/browser/editor/debug-hover-source.d.ts +1 -0
  20. package/lib/browser/editor/debug-hover-source.d.ts.map +1 -1
  21. package/lib/browser/editor/debug-hover-source.js +9 -0
  22. package/lib/browser/editor/debug-hover-source.js.map +1 -1
  23. package/lib/browser/editor/debug-inline-value-decorator.js +1 -1
  24. package/lib/browser/editor/debug-inline-value-decorator.js.map +1 -1
  25. package/lib/browser/model/debug-stack-frame.d.ts +2 -2
  26. package/lib/browser/model/debug-stack-frame.d.ts.map +1 -1
  27. package/lib/browser/model/debug-stack-frame.js +13 -5
  28. package/lib/browser/model/debug-stack-frame.js.map +1 -1
  29. package/lib/browser/model/debug-thread.d.ts +2 -2
  30. package/lib/browser/model/debug-thread.d.ts.map +1 -1
  31. package/lib/browser/model/debug-thread.js +13 -9
  32. package/lib/browser/model/debug-thread.js.map +1 -1
  33. package/lib/browser/view/debug-variables-source.d.ts.map +1 -1
  34. package/lib/browser/view/debug-variables-source.js +1 -0
  35. package/lib/browser/view/debug-variables-source.js.map +1 -1
  36. package/lib/browser/view/debug-variables-widget.d.ts +15 -0
  37. package/lib/browser/view/debug-variables-widget.d.ts.map +1 -1
  38. package/lib/browser/view/debug-variables-widget.js +56 -1
  39. package/lib/browser/view/debug-variables-widget.js.map +1 -1
  40. package/lib/browser/view/debug-view-model.d.ts +4 -0
  41. package/lib/browser/view/debug-view-model.d.ts.map +1 -1
  42. package/lib/browser/view/debug-view-model.js +11 -1
  43. package/lib/browser/view/debug-view-model.js.map +1 -1
  44. package/lib/browser/view/debug-watch-expression.d.ts.map +1 -1
  45. package/lib/browser/view/debug-watch-expression.js +4 -5
  46. package/lib/browser/view/debug-watch-expression.js.map +1 -1
  47. package/lib/browser/view/debug-watch-source.d.ts.map +1 -1
  48. package/lib/browser/view/debug-watch-source.js +1 -0
  49. package/lib/browser/view/debug-watch-source.js.map +1 -1
  50. package/lib/common/debug-preferences.d.ts +1 -0
  51. package/lib/common/debug-preferences.d.ts.map +1 -1
  52. package/lib/common/debug-preferences.js +11 -1
  53. package/lib/common/debug-preferences.js.map +1 -1
  54. package/package.json +15 -15
  55. package/src/browser/console/debug-console-items.tsx +90 -21
  56. package/src/browser/console/debug-console-session.ts +1 -0
  57. package/src/browser/debug-session-contribution.ts +3 -1
  58. package/src/browser/debug-session-manager.ts +10 -0
  59. package/src/browser/debug-session.tsx +13 -1
  60. package/src/browser/editor/debug-hover-source.tsx +6 -2
  61. package/src/browser/editor/debug-inline-value-decorator.ts +1 -1
  62. package/src/browser/model/debug-stack-frame.tsx +13 -6
  63. package/src/browser/model/debug-thread.tsx +14 -10
  64. package/src/browser/style/index.css +17 -0
  65. package/src/browser/view/debug-variables-source.ts +1 -0
  66. package/src/browser/view/debug-variables-widget.ts +59 -0
  67. package/src/browser/view/debug-view-model.ts +13 -0
  68. package/src/browser/view/debug-watch-expression.tsx +5 -6
  69. package/src/browser/view/debug-watch-source.ts +1 -0
  70. package/src/common/debug-preferences.ts +12 -1
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@theia/debug",
3
- "version": "1.67.0-next.59+3f14297ea",
3
+ "version": "1.67.0-next.86+03f92ff1d",
4
4
  "description": "Theia - Debug Extension",
5
5
  "dependencies": {
6
- "@theia/console": "1.67.0-next.59+3f14297ea",
7
- "@theia/core": "1.67.0-next.59+3f14297ea",
8
- "@theia/editor": "1.67.0-next.59+3f14297ea",
9
- "@theia/filesystem": "1.67.0-next.59+3f14297ea",
10
- "@theia/markers": "1.67.0-next.59+3f14297ea",
11
- "@theia/monaco": "1.67.0-next.59+3f14297ea",
6
+ "@theia/console": "1.67.0-next.86+03f92ff1d",
7
+ "@theia/core": "1.67.0-next.86+03f92ff1d",
8
+ "@theia/editor": "1.67.0-next.86+03f92ff1d",
9
+ "@theia/filesystem": "1.67.0-next.86+03f92ff1d",
10
+ "@theia/markers": "1.67.0-next.86+03f92ff1d",
11
+ "@theia/monaco": "1.67.0-next.86+03f92ff1d",
12
12
  "@theia/monaco-editor-core": "1.96.302",
13
- "@theia/output": "1.67.0-next.59+3f14297ea",
14
- "@theia/process": "1.67.0-next.59+3f14297ea",
15
- "@theia/task": "1.67.0-next.59+3f14297ea",
16
- "@theia/terminal": "1.67.0-next.59+3f14297ea",
17
- "@theia/test": "1.67.0-next.59+3f14297ea",
18
- "@theia/variable-resolver": "1.67.0-next.59+3f14297ea",
19
- "@theia/workspace": "1.67.0-next.59+3f14297ea",
13
+ "@theia/output": "1.67.0-next.86+03f92ff1d",
14
+ "@theia/process": "1.67.0-next.86+03f92ff1d",
15
+ "@theia/task": "1.67.0-next.86+03f92ff1d",
16
+ "@theia/terminal": "1.67.0-next.86+03f92ff1d",
17
+ "@theia/test": "1.67.0-next.86+03f92ff1d",
18
+ "@theia/variable-resolver": "1.67.0-next.86+03f92ff1d",
19
+ "@theia/workspace": "1.67.0-next.86+03f92ff1d",
20
20
  "@vscode/debugprotocol": "^1.51.0",
21
21
  "fast-deep-equal": "^3.1.3",
22
22
  "jsonc-parser": "^2.2.0",
@@ -64,5 +64,5 @@
64
64
  "nyc": {
65
65
  "extends": "../../configs/nyc.json"
66
66
  },
67
- "gitHead": "3f14297ea2edcdb1fffd74afee0613e70b43e125"
67
+ "gitHead": "03f92ff1d97dcb199de39b48e60a53535de22808"
68
68
  }
@@ -16,12 +16,12 @@
16
16
 
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol';
19
- import { SingleTextInputDialog } from '@theia/core/lib/browser';
19
+ import { codicon, SingleTextInputDialog } from '@theia/core/lib/browser';
20
20
  import { ConsoleItem, CompositeConsoleItem } from '@theia/console/lib/browser/console-session';
21
21
  import { DebugSession, formatMessage } from '../debug-session';
22
22
  import { Severity } from '@theia/core/lib/common/severity';
23
23
  import * as monaco from '@theia/monaco-editor-core';
24
- import { nls } from '@theia/core';
24
+ import { generateUuid, nls } from '@theia/core';
25
25
 
26
26
  export type DebugSessionProvider = () => DebugSession | undefined;
27
27
 
@@ -34,17 +34,24 @@ export class ExpressionContainer implements CompositeConsoleItem {
34
34
  return this.sessionProvider();
35
35
  }
36
36
 
37
+ readonly id: string | number;
37
38
  protected variablesReference: number;
38
39
  protected namedVariables: number | undefined;
39
40
  protected indexedVariables: number | undefined;
41
+ protected presentationHint: DebugProtocol.VariablePresentationHint | undefined;
40
42
  protected readonly startOfVariables: number;
41
43
 
42
44
  constructor(options: ExpressionContainer.Options) {
43
45
  this.sessionProvider = options.session;
46
+ this.id = options.id ?? generateUuid();
44
47
  this.variablesReference = options.variablesReference || 0;
45
48
  this.namedVariables = options.namedVariables;
46
49
  this.indexedVariables = options.indexedVariables;
47
50
  this.startOfVariables = options.startOfVariables || 0;
51
+ this.presentationHint = options.presentationHint;
52
+ if (this.lazy) {
53
+ (this as CompositeConsoleItem).expandByDefault = () => !this.lazy && !this.session?.autoExpandLazyVariables;
54
+ }
48
55
  }
49
56
 
50
57
  render(): React.ReactNode {
@@ -56,7 +63,31 @@ export class ExpressionContainer implements CompositeConsoleItem {
56
63
  }
57
64
 
58
65
  get hasElements(): boolean {
59
- return this.variablesReference !== undefined;
66
+ return !!this.variablesReference && !this.lazy;
67
+ }
68
+
69
+ get lazy(): boolean {
70
+ return !!this.presentationHint?.lazy;
71
+ }
72
+
73
+ async resolveLazy(): Promise<void> {
74
+ const { session, variablesReference, lazy } = this;
75
+ if (!session || !variablesReference || !lazy) {
76
+ return;
77
+ }
78
+ const response = await session.sendRequest('variables', { variablesReference });
79
+ const { variables } = response.body;
80
+ if (variables.length !== 1) {
81
+ return;
82
+ }
83
+ this.handleResolvedLazy(variables[0]);
84
+ }
85
+
86
+ protected handleResolvedLazy(resolved: DebugProtocol.Variable): void {
87
+ this.variablesReference = resolved.variablesReference;
88
+ this.namedVariables = resolved.namedVariables;
89
+ this.indexedVariables = resolved.indexedVariables;
90
+ this.presentationHint = resolved.presentationHint;
60
91
  }
61
92
 
62
93
  protected elements: Promise<ExpressionContainer[]> | undefined;
@@ -85,13 +116,15 @@ export class ExpressionContainer implements CompositeConsoleItem {
85
116
  const start = this.startOfVariables + i * chunkSize;
86
117
  const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize);
87
118
  const { variablesReference } = this;
119
+ const name = `[${start}..${start + count - 1}]`;
88
120
  result.push(new DebugVirtualVariable({
89
121
  session: this.sessionProvider,
122
+ id: `${this.id}:${name}`,
90
123
  variablesReference,
91
124
  namedVariables: 0,
92
125
  indexedVariables: count,
93
126
  startOfVariables: start,
94
- name: `[${start}..${start + count - 1}]`
127
+ name
95
128
  }));
96
129
  }
97
130
  return result;
@@ -105,14 +138,23 @@ export class ExpressionContainer implements CompositeConsoleItem {
105
138
  protected fetch(result: ConsoleItem[], filter: 'indexed', start: number, count?: number): Promise<void>;
106
139
  protected async fetch(result: ConsoleItem[], filter: 'indexed' | 'named', start?: number, count?: number): Promise<void> {
107
140
  try {
108
- const { variablesReference } = this;
109
- const response = await this.session!.sendRequest('variables', { variablesReference, filter, start, count });
110
- const { variables } = response.body;
111
- const names = new Set<string>();
112
- for (const variable of variables) {
113
- if (!names.has(variable.name)) {
114
- result.push(new DebugVariable(this.sessionProvider, variable, this));
115
- names.add(variable.name);
141
+ const { session } = this;
142
+ if (session) {
143
+ const { variablesReference } = this;
144
+ const response = await session.sendRequest('variables', { variablesReference, filter, start, count });
145
+ const { variables } = response.body;
146
+ const names = new Set<string>();
147
+ const debugVariables: DebugVariable[] = [];
148
+ for (const variable of variables) {
149
+ if (!names.has(variable.name)) {
150
+ const v = new DebugVariable(this.sessionProvider, variable, this);
151
+ debugVariables.push(v);
152
+ result.push(v);
153
+ names.add(variable.name);
154
+ }
155
+ }
156
+ if (session.autoExpandLazyVariables) {
157
+ await Promise.all(debugVariables.map(v => v.lazy && v.resolveLazy()));
116
158
  }
117
159
  }
118
160
  } catch (e) {
@@ -128,10 +170,12 @@ export class ExpressionContainer implements CompositeConsoleItem {
128
170
  export namespace ExpressionContainer {
129
171
  export interface Options {
130
172
  session: DebugSessionProvider,
173
+ id?: string | number,
131
174
  variablesReference?: number
132
175
  namedVariables?: number
133
176
  indexedVariables?: number
134
177
  startOfVariables?: number
178
+ presentationHint?: DebugProtocol.VariablePresentationHint
135
179
  }
136
180
  }
137
181
 
@@ -147,9 +191,11 @@ export class DebugVariable extends ExpressionContainer {
147
191
  ) {
148
192
  super({
149
193
  session,
194
+ id: `${parent.id}:${variable.name}`,
150
195
  variablesReference: variable.variablesReference,
151
196
  namedVariables: variable.namedVariables,
152
- indexedVariables: variable.indexedVariables
197
+ indexedVariables: variable.indexedVariables,
198
+ presentationHint: variable.presentationHint
153
199
  });
154
200
  }
155
201
 
@@ -169,17 +215,20 @@ export class DebugVariable extends ExpressionContainer {
169
215
  }
170
216
 
171
217
  get readOnly(): boolean {
172
- return this.variable.presentationHint?.attributes?.includes('readOnly') ?? false;
218
+ return this.presentationHint?.attributes?.includes('readOnly') || this.lazy;
173
219
  }
174
220
 
175
221
  override render(): React.ReactNode {
176
- const { type, value, name } = this;
222
+ const { type, value, name, lazy } = this;
177
223
  return <div className={this.variableClassName}>
178
- <span title={type || name} className='name' ref={this.setNameRef}>{name}{!!value && ': '}</span>
179
- <span title={value} ref={this.setValueRef}>{value}</span>
224
+ <span title={type || name} className='name' ref={this.setNameRef}>{name}{(value || lazy) && ': '}</span>
225
+ {lazy && <span title={nls.localizeByDefault('Click to expand')} className={codicon('eye') + ' lazy-button'} onClick={this.handleLazyButtonClick} />}
226
+ <span title={value} className='value' ref={this.setValueRef}>{value}</span>
180
227
  </div>;
181
228
  }
182
229
 
230
+ private readonly handleLazyButtonClick = () => this.resolveLazy();
231
+
183
232
  protected get variableClassName(): string {
184
233
  const { type, value } = this;
185
234
  const classNames = ['theia-debug-console-variable'];
@@ -195,6 +244,13 @@ export class DebugVariable extends ExpressionContainer {
195
244
  return classNames.join(' ');
196
245
  }
197
246
 
247
+ protected override handleResolvedLazy(resolved: DebugProtocol.Variable): void {
248
+ this._value = resolved.value;
249
+ this._type = resolved.type || this._type;
250
+ super.handleResolvedLazy(resolved);
251
+ this.session?.['onDidResolveLazyVariableEmitter'].fire(this);
252
+ }
253
+
198
254
  get supportSetVariable(): boolean {
199
255
  return !!this.session && !!this.session.capabilities.supportsSetVariable;
200
256
  }
@@ -312,9 +368,10 @@ export class ExpressionItem extends ExpressionContainer {
312
368
 
313
369
  constructor(
314
370
  protected _expression: string,
315
- session: DebugSessionProvider
371
+ session: DebugSessionProvider,
372
+ id?: string | number
316
373
  ) {
317
- super({ session });
374
+ super({ session, id });
318
375
  }
319
376
 
320
377
  get expression(): string {
@@ -333,7 +390,7 @@ export class ExpressionItem extends ExpressionContainer {
333
390
  </div>;
334
391
  }
335
392
 
336
- async evaluate(context: string = 'repl'): Promise<void> {
393
+ async evaluate(context: string = 'repl', resolveLazy = true): Promise<void> {
337
394
  const session = this.session;
338
395
  if (!session?.currentFrame) {
339
396
  this.setResult(undefined, ExpressionItem.notAvailable);
@@ -343,6 +400,9 @@ export class ExpressionItem extends ExpressionContainer {
343
400
  try {
344
401
  const body = await session.evaluate(this._expression, context);
345
402
  this.setResult(body);
403
+ if (this.lazy && resolveLazy) {
404
+ await this.resolveLazy();
405
+ }
346
406
  } catch (err) {
347
407
  this.setResult(undefined, err.message);
348
408
  }
@@ -356,6 +416,7 @@ export class ExpressionItem extends ExpressionContainer {
356
416
  this.variablesReference = body.variablesReference;
357
417
  this.namedVariables = body.namedVariables;
358
418
  this.indexedVariables = body.indexedVariables;
419
+ this.presentationHint = body.presentationHint;
359
420
  this.severity = Severity.Log;
360
421
  } else {
361
422
  this._value = error;
@@ -364,21 +425,29 @@ export class ExpressionItem extends ExpressionContainer {
364
425
  this.variablesReference = 0;
365
426
  this.namedVariables = undefined;
366
427
  this.indexedVariables = undefined;
428
+ this.presentationHint = undefined;
367
429
  this.severity = Severity.Error;
368
430
  }
369
431
  this.elements = undefined;
370
432
  }
371
433
 
434
+ protected override handleResolvedLazy(resolved: DebugProtocol.Variable): void {
435
+ this._value = resolved.value;
436
+ this._type = resolved.type || this._type;
437
+ super.handleResolvedLazy(resolved);
438
+ }
372
439
  }
373
440
 
374
441
  export class DebugScope extends ExpressionContainer {
375
442
 
376
443
  constructor(
377
444
  protected readonly raw: DebugProtocol.Scope,
378
- session: DebugSessionProvider
445
+ session: DebugSessionProvider,
446
+ id: number
379
447
  ) {
380
448
  super({
381
449
  session,
450
+ id: `${raw.name}:${id}`,
382
451
  variablesReference: raw.variablesReference,
383
452
  namedVariables: raw.namedVariables,
384
453
  indexedVariables: raw.indexedVariables
@@ -85,6 +85,7 @@ export class DebugConsoleSession extends ConsoleSession {
85
85
  triggerCharacters: ['.'],
86
86
  provideCompletionItems: (model, position) => this.completions(model, position),
87
87
  }));
88
+ this.toDispose.push(this.sessionManager.onDidResolveLazyVariable(() => this.fireDidChange()));
88
89
  }
89
90
 
90
91
  getElements(): IterableIterator<ConsoleItem> {
@@ -146,7 +146,9 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory {
146
146
  this.messages,
147
147
  this.fileService,
148
148
  this.debugContributionProvider,
149
- this.workspaceService);
149
+ this.workspaceService,
150
+ this.debugPreferences
151
+ );
150
152
  }
151
153
 
152
154
  protected getTraceOutputChannel(): OutputChannel | undefined {
@@ -39,6 +39,7 @@ import * as monaco from '@theia/monaco-editor-core';
39
39
  import { DebugInstructionBreakpoint } from './model/debug-instruction-breakpoint';
40
40
  import { DebugSessionConfigurationLabelProvider } from './debug-session-configuration-label-provider';
41
41
  import { DebugDataBreakpoint } from './model/debug-data-breakpoint';
42
+ import { DebugVariable } from './console/debug-console-items';
42
43
 
43
44
  export interface WillStartDebugSession extends WaitUntilEvent {
44
45
  }
@@ -57,6 +58,11 @@ export interface DidChangeBreakpointsEvent {
57
58
  uri: URI
58
59
  }
59
60
 
61
+ export interface DidResolveLazyVariableEvent {
62
+ readonly session: DebugSession
63
+ readonly variable: DebugVariable
64
+ }
65
+
60
66
  export interface DebugSessionCustomEvent {
61
67
  readonly body?: any // eslint-disable-line @typescript-eslint/no-explicit-any
62
68
  readonly event: string
@@ -112,6 +118,9 @@ export class DebugSessionManager {
112
118
  this.onDidChangeEmitter.fire(current);
113
119
  }
114
120
 
121
+ protected readonly onDidResolveLazyVariableEmitter = new Emitter<DidResolveLazyVariableEvent>();
122
+ readonly onDidResolveLazyVariable: Event<DidResolveLazyVariableEvent> = this.onDidResolveLazyVariableEmitter.event;
123
+
115
124
  @inject(DebugSessionFactory)
116
125
  protected readonly debugSessionFactory: DebugSessionFactory;
117
126
 
@@ -544,6 +553,7 @@ export class DebugSessionManager {
544
553
  }
545
554
  this.fireDidChange(current);
546
555
  }));
556
+ this.disposeOnCurrentSessionChanged.push(current.onDidResolveLazyVariable(variable => this.onDidResolveLazyVariableEmitter.fire({ session: current, variable })));
547
557
  this.disposeOnCurrentSessionChanged.push(current.onDidFocusStackFrame(frame => this.onDidFocusStackFrameEmitter.fire(frame)));
548
558
  this.disposeOnCurrentSessionChanged.push(current.onDidFocusThread(thread => this.onDidFocusThreadEmitter.fire(thread)));
549
559
  const { currentThread } = current;
@@ -25,7 +25,7 @@ import { EditorManager } from '@theia/editor/lib/browser';
25
25
  import { CompositeTreeElement } from '@theia/core/lib/browser/source-tree';
26
26
  import { DebugSessionConnection, DebugRequestTypes, DebugEventTypes } from './debug-session-connection';
27
27
  import { DebugThread, StoppedDetails, DebugThreadData } from './model/debug-thread';
28
- import { DebugScope } from './console/debug-console-items';
28
+ import { DebugScope, DebugVariable } from './console/debug-console-items';
29
29
  import { DebugStackFrame } from './model/debug-stack-frame';
30
30
  import { DebugSource } from './model/debug-source';
31
31
  import { DebugBreakpoint, DebugBreakpointOptions } from './model/debug-breakpoint';
@@ -47,6 +47,7 @@ import { nls } from '@theia/core';
47
47
  import { TestService, TestServices } from '@theia/test/lib/browser/test-service';
48
48
  import { DebugSessionManager } from './debug-session-manager';
49
49
  import { DebugDataBreakpoint } from './model/debug-data-breakpoint';
50
+ import { DebugPreferences } from '../common/debug-preferences';
50
51
 
51
52
  export enum DebugState {
52
53
  Inactive,
@@ -103,6 +104,9 @@ export class DebugSession implements CompositeTreeElement {
103
104
  this.onDidChangeBreakpointsEmitter.fire(uri);
104
105
  }
105
106
 
107
+ protected readonly onDidResolveLazyVariableEmitter = new Emitter<DebugVariable>();
108
+ readonly onDidResolveLazyVariable: Event<DebugVariable> = this.onDidResolveLazyVariableEmitter.event;
109
+
106
110
  protected readonly childSessions = new Map<string, DebugSession>();
107
111
  protected readonly toDispose = new DisposableCollection();
108
112
 
@@ -124,6 +128,7 @@ export class DebugSession implements CompositeTreeElement {
124
128
  protected readonly fileService: FileService,
125
129
  protected readonly debugContributionProvider: ContributionProvider<DebugContribution>,
126
130
  protected readonly workspaceService: WorkspaceService,
131
+ protected readonly debugPreferences: DebugPreferences,
127
132
  /**
128
133
  * Number of millis after a `stop` request times out. It's 5 seconds by default.
129
134
  */
@@ -157,7 +162,10 @@ export class DebugSession implements CompositeTreeElement {
157
162
  this.connection.onDidClose(() => this.toDispose.dispose());
158
163
  this.toDispose.pushAll([
159
164
  this.onDidChangeEmitter,
165
+ this.onDidFocusStackFrameEmitter,
166
+ this.onDidFocusThreadEmitter,
160
167
  this.onDidChangeBreakpointsEmitter,
168
+ this.onDidResolveLazyVariableEmitter,
161
169
  Disposable.create(() => {
162
170
  this.clearBreakpoints();
163
171
  this.doUpdateThreads([]);
@@ -186,6 +194,10 @@ export class DebugSession implements CompositeTreeElement {
186
194
  return this._capabilities;
187
195
  }
188
196
 
197
+ get autoExpandLazyVariables(): boolean {
198
+ return this.debugPreferences['debug.autoExpandLazyVariables'] === 'on';
199
+ }
200
+
189
201
  protected readonly sources = new Map<string, DebugSource>();
190
202
  getSource(raw: DebugProtocol.Source): DebugSource {
191
203
  const uri = DebugSource.toUri(raw).toString();
@@ -18,7 +18,7 @@ import * as React from '@theia/core/shared/react';
18
18
  import { TreeSource, TreeElement } from '@theia/core/lib/browser/source-tree';
19
19
  import { ExpressionContainer, ExpressionItem, DebugVariable } from '../console/debug-console-items';
20
20
  import { DebugSessionManager } from '../debug-session-manager';
21
- import { injectable, inject } from '@theia/core/shared/inversify';
21
+ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
22
22
 
23
23
  @injectable()
24
24
  export class DebugHoverSource extends TreeSource {
@@ -36,6 +36,11 @@ export class DebugHoverSource extends TreeSource {
36
36
  return this.elements[Symbol.iterator]();
37
37
  }
38
38
 
39
+ @postConstruct()
40
+ init(): void {
41
+ this.toDispose.push(this.sessions.onDidResolveLazyVariable(() => this.fireDidChange()));
42
+ }
43
+
39
44
  protected renderTitle(element: ExpressionItem | DebugVariable): React.ReactNode {
40
45
  return <div className='theia-debug-hover-title' title={element.value}>{element.value}</div>;
41
46
  }
@@ -101,5 +106,4 @@ export class DebugHoverSource extends TreeSource {
101
106
  return this.doFindVariable(variables[0], namesToFind.slice(1));
102
107
  }
103
108
  }
104
-
105
109
  }
@@ -194,7 +194,7 @@ export class DebugInlineValueDecorator implements FrontendApplicationContributio
194
194
  }
195
195
  if (expr) {
196
196
  const expression = new ExpressionItem(expr, () => stackFrame.thread.session);
197
- await expression.evaluate('watch');
197
+ await expression.evaluate('watch', false);
198
198
  if (expression.available) {
199
199
  text = this.formatInlineValue(expr, expression.value);
200
200
  }
@@ -31,6 +31,7 @@ import { RecursivePartial } from '@theia/core';
31
31
  import { DebugSession } from '../debug-session';
32
32
  import { DebugThread } from './debug-thread';
33
33
  import * as monaco from '@theia/monaco-editor-core';
34
+ import { stringHash } from '@theia/core/lib/common/hash';
34
35
 
35
36
  export class DebugStackFrameData {
36
37
  readonly raw: DebugProtocol.StackFrame;
@@ -40,15 +41,12 @@ export class DebugStackFrame extends DebugStackFrameData implements TreeElement
40
41
 
41
42
  constructor(
42
43
  readonly thread: DebugThread,
43
- readonly session: DebugSession
44
+ readonly session: DebugSession,
45
+ readonly id: string
44
46
  ) {
45
47
  super();
46
48
  }
47
49
 
48
- get id(): string {
49
- return this.session.id + ':' + this.thread.id + ':' + this.raw.id;
50
- }
51
-
52
50
  /**
53
51
  * Returns the frame identifier from the debug protocol.
54
52
  */
@@ -115,7 +113,16 @@ export class DebugStackFrame extends DebugStackFrameData implements TreeElement
115
113
  if (!response) {
116
114
  return [];
117
115
  }
118
- return response.body.scopes.map(raw => new DebugScope(raw, () => this.session));
116
+ const scopeIds = new Set<number>();
117
+ return response.body.scopes.map(raw => {
118
+ // just as in VS Code, the id is based on the name and location to retain expansion state across multiple pauses
119
+ let id = 0;
120
+ do {
121
+ id = stringHash(`${raw.name}:${raw.line}:${raw.column}`, id);
122
+ } while (scopeIds.has(id));
123
+ scopeIds.add(id);
124
+ return new DebugScope(raw, () => this.session, id);
125
+ });
119
126
  }
120
127
 
121
128
  // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/workbench/contrib/debug/common/debugModel.ts#L324-L335
@@ -164,7 +164,7 @@ export class DebugThread extends DebugThreadData implements TreeElement {
164
164
  }
165
165
  }
166
166
 
167
- protected readonly _frames = new Map<number, DebugStackFrame>();
167
+ protected readonly _frames = new Map<string, DebugStackFrame>();
168
168
  get frames(): IterableIterator<DebugStackFrame> {
169
169
  return this._frames.values();
170
170
  }
@@ -189,7 +189,7 @@ export class DebugThread extends DebugThreadData implements TreeElement {
189
189
  if (cancel.isCancellationRequested) {
190
190
  return [];
191
191
  }
192
- return this.doUpdateFrames(frames);
192
+ return this.doUpdateFrames(start, frames);
193
193
  } catch (e) {
194
194
  console.error('fetchFrames failed:', e);
195
195
  return [];
@@ -219,15 +219,19 @@ export class DebugThread extends DebugThreadData implements TreeElement {
219
219
  return [];
220
220
  }
221
221
  }
222
- protected doUpdateFrames(frames: DebugProtocol.StackFrame[]): DebugStackFrame[] {
222
+ protected doUpdateFrames(startFrame: number, frames: DebugProtocol.StackFrame[]): DebugStackFrame[] {
223
223
  const result = new Set<DebugStackFrame>();
224
- for (const raw of frames) {
225
- const id = raw.id;
226
- const frame = this._frames.get(id) || new DebugStackFrame(this, this.session);
224
+ frames.forEach((raw, index) => {
225
+ // Similarly to VS Code, we no longer use `raw.frameId` in computation of the DebugStackFrame id.
226
+ // The `raw.frameId` was changing in every step and was not allowing us to correlate stack frames between step events.
227
+ // Refs: https://github.com/microsoft/vscode/commit/18fc2bcf718e22265b5e09bb7fd0e9c090264cd2, https://github.com/microsoft/vscode/issues/93230#issuecomment-642558395
228
+ const source = raw.source && this.session.getSource(raw.source);
229
+ const id = `${this.id}:${startFrame + index}:${source?.name}`;
230
+ const frame = this._frames.get(id) || new DebugStackFrame(this, this.session, id);
227
231
  this._frames.set(id, frame);
228
232
  frame.update({ raw });
229
233
  result.add(frame);
230
- }
234
+ });
231
235
  this.updateCurrentFrame();
232
236
  return [...result.values()];
233
237
  }
@@ -247,9 +251,9 @@ export class DebugThread extends DebugThreadData implements TreeElement {
247
251
  }
248
252
  protected updateCurrentFrame(): void {
249
253
  const { currentFrame } = this;
250
- const frameId = currentFrame && currentFrame.raw.id;
251
- this.currentFrame = typeof frameId === 'number' &&
252
- this._frames.get(frameId) ||
254
+ const id = currentFrame && currentFrame.id;
255
+ this.currentFrame = typeof id === 'string' &&
256
+ this._frames.get(id) ||
253
257
  this._frames.values().next().value;
254
258
  }
255
259
 
@@ -187,6 +187,7 @@
187
187
  }
188
188
 
189
189
  .theia-debug-console-variable {
190
+ display: flex;
190
191
  white-space: nowrap;
191
192
  overflow: hidden;
192
193
  text-overflow: ellipsis;
@@ -212,6 +213,22 @@
212
213
  color: var(--theia-variable-name-color);
213
214
  }
214
215
 
216
+ .theia-debug-console-variable .lazy-button {
217
+ margin-left: 3px;
218
+ border-radius: 5px;
219
+ cursor: pointer;
220
+ align-self: center;
221
+ color: var(--theia-icon-foreground);
222
+ }
223
+
224
+ .theia-debug-console-variable .lazy-button:hover {
225
+ background-color: var(--theia-toolbar-hoverBackground);
226
+ }
227
+
228
+ .theia-debug-console-variable .value {
229
+ margin-left: 6px;
230
+ }
231
+
215
232
  .theia-TreeNode:not(:hover) .theia-debug-console-variable .action-label {
216
233
  visibility: hidden;
217
234
  }
@@ -30,6 +30,7 @@ export class DebugVariablesSource extends TreeSource {
30
30
  protected init(): void {
31
31
  this.refresh();
32
32
  this.toDispose.push(this.model.onDidChange(() => this.refresh()));
33
+ this.toDispose.push(this.model.onDidResolveLazyVariable(() => this.fireDidChange()));
33
34
  }
34
35
 
35
36
  protected readonly refresh = debounce(() => this.fireDidChange(), 400);