@theia/debug 1.67.0-next.59 → 1.67.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 (145) hide show
  1. package/lib/browser/breakpoint/breakpoint-manager.js +1 -1
  2. package/lib/browser/breakpoint/breakpoint-manager.js.map +1 -1
  3. package/lib/browser/console/debug-console-items.d.ts +13 -3
  4. package/lib/browser/console/debug-console-items.d.ts.map +1 -1
  5. package/lib/browser/console/debug-console-items.js +81 -20
  6. package/lib/browser/console/debug-console-items.js.map +1 -1
  7. package/lib/browser/console/debug-console-session.d.ts.map +1 -1
  8. package/lib/browser/console/debug-console-session.js +1 -0
  9. package/lib/browser/console/debug-console-session.js.map +1 -1
  10. package/lib/browser/debug-commands.d.ts +153 -0
  11. package/lib/browser/debug-commands.d.ts.map +1 -0
  12. package/lib/browser/debug-commands.js +399 -0
  13. package/lib/browser/debug-commands.js.map +1 -0
  14. package/lib/browser/debug-frontend-application-contribution.d.ts +1 -150
  15. package/lib/browser/debug-frontend-application-contribution.d.ts.map +1 -1
  16. package/lib/browser/debug-frontend-application-contribution.js +155 -532
  17. package/lib/browser/debug-frontend-application-contribution.js.map +1 -1
  18. package/lib/browser/debug-prefix-configuration.js +3 -3
  19. package/lib/browser/debug-prefix-configuration.js.map +1 -1
  20. package/lib/browser/debug-session-contribution.d.ts +2 -1
  21. package/lib/browser/debug-session-contribution.d.ts.map +1 -1
  22. package/lib/browser/debug-session-contribution.js +5 -1
  23. package/lib/browser/debug-session-contribution.js.map +1 -1
  24. package/lib/browser/debug-session-manager.d.ts +11 -3
  25. package/lib/browser/debug-session-manager.d.ts.map +1 -1
  26. package/lib/browser/debug-session-manager.js +49 -8
  27. package/lib/browser/debug-session-manager.js.map +1 -1
  28. package/lib/browser/debug-session.d.ts +9 -3
  29. package/lib/browser/debug-session.d.ts.map +1 -1
  30. package/lib/browser/debug-session.js +13 -3
  31. package/lib/browser/debug-session.js.map +1 -1
  32. package/lib/browser/editor/debug-hover-source.d.ts +1 -0
  33. package/lib/browser/editor/debug-hover-source.d.ts.map +1 -1
  34. package/lib/browser/editor/debug-hover-source.js +9 -0
  35. package/lib/browser/editor/debug-hover-source.js.map +1 -1
  36. package/lib/browser/editor/debug-inline-value-decorator.js +1 -1
  37. package/lib/browser/editor/debug-inline-value-decorator.js.map +1 -1
  38. package/lib/browser/model/debug-breakpoint.d.ts +4 -2
  39. package/lib/browser/model/debug-breakpoint.d.ts.map +1 -1
  40. package/lib/browser/model/debug-breakpoint.js +10 -2
  41. package/lib/browser/model/debug-breakpoint.js.map +1 -1
  42. package/lib/browser/model/debug-data-breakpoint.d.ts +2 -0
  43. package/lib/browser/model/debug-data-breakpoint.d.ts.map +1 -1
  44. package/lib/browser/model/debug-data-breakpoint.js +16 -5
  45. package/lib/browser/model/debug-data-breakpoint.js.map +1 -1
  46. package/lib/browser/model/debug-function-breakpoint.d.ts +3 -0
  47. package/lib/browser/model/debug-function-breakpoint.d.ts.map +1 -1
  48. package/lib/browser/model/debug-function-breakpoint.js +17 -1
  49. package/lib/browser/model/debug-function-breakpoint.js.map +1 -1
  50. package/lib/browser/model/debug-instruction-breakpoint.d.ts +2 -0
  51. package/lib/browser/model/debug-instruction-breakpoint.d.ts.map +1 -1
  52. package/lib/browser/model/debug-instruction-breakpoint.js +13 -1
  53. package/lib/browser/model/debug-instruction-breakpoint.js.map +1 -1
  54. package/lib/browser/model/debug-source-breakpoint.d.ts +6 -1
  55. package/lib/browser/model/debug-source-breakpoint.d.ts.map +1 -1
  56. package/lib/browser/model/debug-source-breakpoint.js +17 -1
  57. package/lib/browser/model/debug-source-breakpoint.js.map +1 -1
  58. package/lib/browser/model/debug-stack-frame.d.ts +2 -2
  59. package/lib/browser/model/debug-stack-frame.d.ts.map +1 -1
  60. package/lib/browser/model/debug-stack-frame.js +13 -5
  61. package/lib/browser/model/debug-stack-frame.js.map +1 -1
  62. package/lib/browser/model/debug-thread.d.ts +2 -2
  63. package/lib/browser/model/debug-thread.d.ts.map +1 -1
  64. package/lib/browser/model/debug-thread.js +13 -9
  65. package/lib/browser/model/debug-thread.js.map +1 -1
  66. package/lib/browser/view/debug-action.d.ts +1 -0
  67. package/lib/browser/view/debug-action.d.ts.map +1 -1
  68. package/lib/browser/view/debug-action.js +2 -2
  69. package/lib/browser/view/debug-action.js.map +1 -1
  70. package/lib/browser/view/debug-breakpoints-source.d.ts +2 -0
  71. package/lib/browser/view/debug-breakpoints-source.d.ts.map +1 -1
  72. package/lib/browser/view/debug-breakpoints-source.js +6 -1
  73. package/lib/browser/view/debug-breakpoints-source.js.map +1 -1
  74. package/lib/browser/view/debug-configuration-widget.js +2 -2
  75. package/lib/browser/view/debug-configuration-widget.js.map +1 -1
  76. package/lib/browser/view/debug-exception-breakpoint.d.ts +9 -2
  77. package/lib/browser/view/debug-exception-breakpoint.d.ts.map +1 -1
  78. package/lib/browser/view/debug-exception-breakpoint.js +26 -5
  79. package/lib/browser/view/debug-exception-breakpoint.js.map +1 -1
  80. package/lib/browser/view/debug-session-widget.d.ts +0 -1
  81. package/lib/browser/view/debug-session-widget.d.ts.map +1 -1
  82. package/lib/browser/view/debug-session-widget.js +0 -4
  83. package/lib/browser/view/debug-session-widget.js.map +1 -1
  84. package/lib/browser/view/debug-threads-widget.d.ts +6 -1
  85. package/lib/browser/view/debug-threads-widget.d.ts.map +1 -1
  86. package/lib/browser/view/debug-threads-widget.js +69 -5
  87. package/lib/browser/view/debug-threads-widget.js.map +1 -1
  88. package/lib/browser/view/debug-toolbar-widget.d.ts +5 -18
  89. package/lib/browser/view/debug-toolbar-widget.d.ts.map +1 -1
  90. package/lib/browser/view/debug-toolbar-widget.js +14 -58
  91. package/lib/browser/view/debug-toolbar-widget.js.map +1 -1
  92. package/lib/browser/view/debug-variables-source.d.ts.map +1 -1
  93. package/lib/browser/view/debug-variables-source.js +1 -0
  94. package/lib/browser/view/debug-variables-source.js.map +1 -1
  95. package/lib/browser/view/debug-variables-widget.d.ts +15 -0
  96. package/lib/browser/view/debug-variables-widget.d.ts.map +1 -1
  97. package/lib/browser/view/debug-variables-widget.js +56 -1
  98. package/lib/browser/view/debug-variables-widget.js.map +1 -1
  99. package/lib/browser/view/debug-view-model.d.ts +4 -0
  100. package/lib/browser/view/debug-view-model.d.ts.map +1 -1
  101. package/lib/browser/view/debug-view-model.js +18 -9
  102. package/lib/browser/view/debug-view-model.js.map +1 -1
  103. package/lib/browser/view/debug-watch-expression.d.ts.map +1 -1
  104. package/lib/browser/view/debug-watch-expression.js +4 -5
  105. package/lib/browser/view/debug-watch-expression.js.map +1 -1
  106. package/lib/browser/view/debug-watch-source.d.ts.map +1 -1
  107. package/lib/browser/view/debug-watch-source.js +1 -0
  108. package/lib/browser/view/debug-watch-source.js.map +1 -1
  109. package/lib/common/debug-preferences.d.ts +1 -0
  110. package/lib/common/debug-preferences.d.ts.map +1 -1
  111. package/lib/common/debug-preferences.js +11 -1
  112. package/lib/common/debug-preferences.js.map +1 -1
  113. package/package.json +16 -16
  114. package/src/browser/breakpoint/breakpoint-manager.ts +1 -1
  115. package/src/browser/console/debug-console-items.tsx +90 -21
  116. package/src/browser/console/debug-console-session.ts +1 -0
  117. package/src/browser/debug-commands.ts +402 -0
  118. package/src/browser/debug-frontend-application-contribution.ts +35 -410
  119. package/src/browser/debug-prefix-configuration.ts +1 -1
  120. package/src/browser/debug-session-contribution.ts +7 -2
  121. package/src/browser/debug-session-manager.ts +62 -9
  122. package/src/browser/debug-session.tsx +17 -4
  123. package/src/browser/editor/debug-hover-source.tsx +6 -2
  124. package/src/browser/editor/debug-inline-value-decorator.ts +1 -1
  125. package/src/browser/model/debug-breakpoint.tsx +12 -3
  126. package/src/browser/model/debug-data-breakpoint.tsx +21 -6
  127. package/src/browser/model/debug-function-breakpoint.tsx +22 -1
  128. package/src/browser/model/debug-instruction-breakpoint.tsx +16 -1
  129. package/src/browser/model/debug-source-breakpoint.tsx +24 -3
  130. package/src/browser/model/debug-stack-frame.tsx +13 -6
  131. package/src/browser/model/debug-thread.tsx +14 -10
  132. package/src/browser/style/index.css +30 -5
  133. package/src/browser/view/debug-action.tsx +3 -2
  134. package/src/browser/view/debug-breakpoints-source.tsx +5 -1
  135. package/src/browser/view/debug-configuration-widget.tsx +1 -1
  136. package/src/browser/view/debug-exception-breakpoint.tsx +30 -5
  137. package/src/browser/view/debug-session-widget.ts +0 -5
  138. package/src/browser/view/debug-threads-widget.ts +84 -6
  139. package/src/browser/view/debug-toolbar-widget.tsx +13 -60
  140. package/src/browser/view/debug-variables-source.ts +1 -0
  141. package/src/browser/view/debug-variables-widget.ts +59 -0
  142. package/src/browser/view/debug-view-model.ts +20 -8
  143. package/src/browser/view/debug-watch-expression.tsx +5 -6
  144. package/src/browser/view/debug-watch-source.ts +1 -0
  145. package/src/common/debug-preferences.ts +12 -1
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { DisposableCollection, Emitter, Event, MessageService, nls, ProgressService, WaitUntilEvent } from '@theia/core';
17
+ import { CommandService, DisposableCollection, Emitter, Event, MessageService, nls, ProgressService, WaitUntilEvent } from '@theia/core';
18
18
  import { LabelProvider, ApplicationShell, ConfirmDialog } from '@theia/core/lib/browser';
19
19
  import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
20
20
  import URI from '@theia/core/lib/common/uri';
@@ -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
 
@@ -124,6 +133,9 @@ export class DebugSessionManager {
124
133
  @inject(EditorManager)
125
134
  protected readonly editorManager: EditorManager;
126
135
 
136
+ @inject(CommandService)
137
+ protected commandService: CommandService;
138
+
127
139
  @inject(BreakpointManager)
128
140
  protected readonly breakpoints: BreakpointManager;
129
141
 
@@ -417,9 +429,15 @@ export class DebugSessionManager {
417
429
  state = session.state;
418
430
  if (state === DebugState.Stopped) {
419
431
  this.onDidStopDebugSessionEmitter.fire(session);
432
+ // Only switch to this session if a thread actually stopped (not just state change)
433
+ if (session.currentThread && session.currentThread.stopped) {
434
+ this.updateCurrentSession(session);
435
+ }
420
436
  }
421
437
  }
422
- this.updateCurrentSession(session);
438
+ // Always fire change event to update views (threads, variables, etc.)
439
+ // The selection logic in widgets will handle not jumping to non-stopped threads
440
+ this.fireDidChange(session);
423
441
  });
424
442
  session.onDidChangeBreakpoints(uri => this.fireDidChangeBreakpoints({ session, uri }));
425
443
  session.on('terminated', async event => {
@@ -438,7 +456,14 @@ export class DebugSessionManager {
438
456
  });
439
457
 
440
458
  session.onDispose(() => this.cleanup(session));
441
- session.start().then(() => this.onDidStartDebugSessionEmitter.fire(session)).catch(e => {
459
+ session.start().then(() => {
460
+ this.onDidStartDebugSessionEmitter.fire(session);
461
+ // Set as current session if no current session exists
462
+ // This ensures the UI shows the running session and buttons are enabled
463
+ if (!this.currentSession) {
464
+ this.updateCurrentSession(session);
465
+ }
466
+ }).catch(e => {
442
467
  session.stop(false, () => {
443
468
  this.debug.terminateDebugSession(session.id);
444
469
  });
@@ -544,6 +569,7 @@ export class DebugSessionManager {
544
569
  }
545
570
  this.fireDidChange(current);
546
571
  }));
572
+ this.disposeOnCurrentSessionChanged.push(current.onDidResolveLazyVariable(variable => this.onDidResolveLazyVariableEmitter.fire({ session: current, variable })));
547
573
  this.disposeOnCurrentSessionChanged.push(current.onDidFocusStackFrame(frame => this.onDidFocusStackFrameEmitter.fire(frame)));
548
574
  this.disposeOnCurrentSessionChanged.push(current.onDidFocusThread(thread => this.onDidFocusThreadEmitter.fire(thread)));
549
575
  const { currentThread } = current;
@@ -598,7 +624,7 @@ export class DebugSessionManager {
598
624
  return currentThread && currentThread.topFrame;
599
625
  }
600
626
 
601
- getFunctionBreakpoints(session: DebugSession | undefined = this.currentSession): DebugFunctionBreakpoint[] {
627
+ getFunctionBreakpoints(session?: DebugSession): DebugFunctionBreakpoint[] {
602
628
  if (session && session.state > DebugState.Initializing) {
603
629
  return session.getFunctionBreakpoints();
604
630
  }
@@ -606,7 +632,7 @@ export class DebugSessionManager {
606
632
  return this.breakpoints.getFunctionBreakpoints().map(origin => new DebugFunctionBreakpoint(origin, { labelProvider, breakpoints, editorManager }));
607
633
  }
608
634
 
609
- getInstructionBreakpoints(session = this.currentSession): DebugInstructionBreakpoint[] {
635
+ getInstructionBreakpoints(session?: DebugSession): DebugInstructionBreakpoint[] {
610
636
  if (session && session.state > DebugState.Initializing) {
611
637
  return session.getInstructionBreakpoints();
612
638
  }
@@ -626,12 +652,39 @@ export class DebugSessionManager {
626
652
  getBreakpoints(uri: URI, session?: DebugSession): DebugSourceBreakpoint[];
627
653
  getBreakpoints(arg?: URI | DebugSession, arg2?: DebugSession): DebugSourceBreakpoint[] {
628
654
  const uri = arg instanceof URI ? arg : undefined;
629
- const session = arg instanceof DebugSession ? arg : arg2 instanceof DebugSession ? arg2 : this.currentSession;
655
+ const session = arg instanceof DebugSession ? arg : arg2 instanceof DebugSession ? arg2 : undefined;
630
656
  if (session && session.state > DebugState.Initializing) {
631
657
  return session.getSourceBreakpoints(uri);
632
658
  }
659
+
660
+ const activeSessions = this.sessions.filter(s => s.state > DebugState.Initializing);
661
+
662
+ // Start with all breakpoints from markers (not installed = shows as filled circle)
633
663
  const { labelProvider, breakpoints, editorManager } = this;
634
- return this.breakpoints.findMarkers({ uri }).map(({ data }) => new DebugSourceBreakpoint(data, { labelProvider, breakpoints, editorManager }));
664
+ const breakpointMap = new Map<string, DebugSourceBreakpoint>();
665
+ const markers = this.breakpoints.findMarkers({ uri });
666
+
667
+ for (const { data } of markers) {
668
+ const bp = new DebugSourceBreakpoint(data, { labelProvider, breakpoints, editorManager }, this.commandService);
669
+ breakpointMap.set(bp.id, bp);
670
+ }
671
+
672
+ // Overlay with VERIFIED breakpoints from active sessions only
673
+ // We only replace a marker-based breakpoint if the session has VERIFIED it
674
+ // This ensures breakpoints show as filled (not installed) rather than hollow (installed but unverified)
675
+ for (const activeSession of activeSessions) {
676
+ const sessionBps = activeSession.getSourceBreakpoints(uri);
677
+
678
+ for (const bp of sessionBps) {
679
+ if (bp.verified) {
680
+ // Session has verified this breakpoint - use the session's version
681
+ breakpointMap.set(bp.id, bp);
682
+ }
683
+ // If not verified, keep the marker-based one (shows as not installed = filled circle)
684
+ }
685
+ }
686
+
687
+ return Array.from(breakpointMap.values());
635
688
  }
636
689
 
637
690
  getLineBreakpoints(uri: URI, line: number): DebugSourceBreakpoint[] {
@@ -641,7 +694,7 @@ export class DebugSessionManager {
641
694
  }
642
695
  const { labelProvider, breakpoints, editorManager } = this;
643
696
  return this.breakpoints.getLineBreakpoints(uri, line).map(origin =>
644
- new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager })
697
+ new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager }, this.commandService)
645
698
  );
646
699
  }
647
700
 
@@ -652,7 +705,7 @@ export class DebugSessionManager {
652
705
  }
653
706
  const origin = this.breakpoints.getInlineBreakpoint(uri, line, column);
654
707
  const { labelProvider, breakpoints, editorManager } = this;
655
- return origin && new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager });
708
+ return origin && new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager }, this.commandService);
656
709
  }
657
710
 
658
711
  /**
@@ -19,13 +19,13 @@
19
19
  import * as React from '@theia/core/shared/react';
20
20
  import { LabelProvider } from '@theia/core/lib/browser';
21
21
  import { DebugProtocol } from '@vscode/debugprotocol';
22
- import { Emitter, Event, DisposableCollection, Disposable, MessageClient, MessageType, Mutable, ContributionProvider } from '@theia/core/lib/common';
22
+ import { Emitter, Event, DisposableCollection, Disposable, MessageClient, MessageType, Mutable, ContributionProvider, CommandService } from '@theia/core/lib/common';
23
23
  import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
24
24
  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,8 @@ 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,
132
+ protected readonly commandService: CommandService,
127
133
  /**
128
134
  * Number of millis after a `stop` request times out. It's 5 seconds by default.
129
135
  */
@@ -157,7 +163,10 @@ export class DebugSession implements CompositeTreeElement {
157
163
  this.connection.onDidClose(() => this.toDispose.dispose());
158
164
  this.toDispose.pushAll([
159
165
  this.onDidChangeEmitter,
166
+ this.onDidFocusStackFrameEmitter,
167
+ this.onDidFocusThreadEmitter,
160
168
  this.onDidChangeBreakpointsEmitter,
169
+ this.onDidResolveLazyVariableEmitter,
161
170
  Disposable.create(() => {
162
171
  this.clearBreakpoints();
163
172
  this.doUpdateThreads([]);
@@ -186,6 +195,10 @@ export class DebugSession implements CompositeTreeElement {
186
195
  return this._capabilities;
187
196
  }
188
197
 
198
+ get autoExpandLazyVariables(): boolean {
199
+ return this.debugPreferences['debug.autoExpandLazyVariables'] === 'on';
200
+ }
201
+
189
202
  protected readonly sources = new Map<string, DebugSource>();
190
203
  getSource(raw: DebugProtocol.Source): DebugSource {
191
204
  const uri = DebugSource.toUri(raw).toString();
@@ -675,7 +688,7 @@ export class DebugSession implements CompositeTreeElement {
675
688
  const origin = SourceBreakpoint.create(uri, { line: raw.line, column: raw.column });
676
689
  if (this.breakpoints.addBreakpoint(origin)) {
677
690
  const breakpoints = this.getSourceBreakpoints(uri);
678
- const breakpoint = new DebugSourceBreakpoint(origin, this.asDebugBreakpointOptions());
691
+ const breakpoint = new DebugSourceBreakpoint(origin, this.asDebugBreakpointOptions(), this.commandService);
679
692
  breakpoint.update({ raw });
680
693
  breakpoints.push(breakpoint);
681
694
  this.setSourceBreakpoints(uri, breakpoints);
@@ -812,7 +825,7 @@ export class DebugSession implements CompositeTreeElement {
812
825
  const known = this._breakpoints.get(affectedUri.toString());
813
826
  const all = this.breakpoints.findMarkers({ uri: affectedUri }).map(({ data }) =>
814
827
  known?.find((candidate): candidate is DebugSourceBreakpoint => candidate instanceof DebugSourceBreakpoint && candidate.origin.id === data.id) ??
815
- new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions())
828
+ new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions(), this.commandService)
816
829
  );
817
830
  const enabled = all.filter(b => b.enabled);
818
831
  try {
@@ -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
  }
@@ -18,8 +18,9 @@ import * as React from '@theia/core/shared/react';
18
18
  import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol';
19
19
  import URI from '@theia/core/lib/common/uri';
20
20
  import { EditorManager } from '@theia/editor/lib/browser';
21
- import { LabelProvider, DISABLED_CLASS } from '@theia/core/lib/browser';
21
+ import { LabelProvider, DISABLED_CLASS, TreeWidget } from '@theia/core/lib/browser';
22
22
  import { TreeElement } from '@theia/core/lib/browser/source-tree';
23
+ import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
23
24
  import { DebugSession } from '../debug-session';
24
25
  import { BaseBreakpoint } from '../breakpoint/breakpoint-marker';
25
26
  import { BreakpointManager } from '../breakpoint/breakpoint-manager';
@@ -44,6 +45,7 @@ export class DebugBreakpointDecoration {
44
45
  export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint> extends DebugBreakpointOptions implements TreeElement {
45
46
 
46
47
  readonly raw?: DebugProtocol.Breakpoint;
48
+ protected treeWidget?: TreeWidget;
47
49
 
48
50
  constructor(
49
51
  readonly uri: URI,
@@ -76,7 +78,7 @@ export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint>
76
78
  }
77
79
 
78
80
  get verified(): boolean {
79
- return !!this.raw ? this.raw.verified : true;
81
+ return !!this.raw ? this.raw.verified : false;
80
82
  }
81
83
 
82
84
  get message(): string {
@@ -91,7 +93,8 @@ export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint>
91
93
  this.setEnabled(event.target.checked);
92
94
  };
93
95
 
94
- render(): React.ReactNode {
96
+ render(host: TreeWidget): React.ReactNode {
97
+ this.treeWidget = host;
95
98
  const classNames = ['theia-source-breakpoint'];
96
99
  if (!this.isEnabled()) {
97
100
  classNames.push(DISABLED_CLASS);
@@ -149,4 +152,10 @@ export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint>
149
152
 
150
153
  protected abstract getBreakpointDecoration(message?: string[]): DebugBreakpointDecoration;
151
154
 
155
+ protected async selectInTree(): Promise<void> {
156
+ if (this.treeWidget?.model && SelectableTreeNode.is(this)) {
157
+ this.treeWidget.model.selectNode(this);
158
+ }
159
+ }
160
+
152
161
  }
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { nls } from '@theia/core';
18
18
  import * as React from '@theia/core/shared/react';
19
+ import { codicon } from '@theia/core/lib/browser';
19
20
  import { BreakpointManager } from '../breakpoint/breakpoint-manager';
20
21
  import { DataBreakpoint } from '../breakpoint/breakpoint-marker';
21
22
  import { DebugBreakpoint, DebugBreakpointDecoration, DebugBreakpointOptions } from './debug-breakpoint';
@@ -44,12 +45,26 @@ export class DebugDataBreakpoint extends DebugBreakpoint<DataBreakpoint> {
44
45
  }
45
46
 
46
47
  protected doRender(): React.ReactNode {
47
- return <span className="line-info theia-data-breakpoint" title={this.origin.info.description}>
48
- <span className="name">{this.origin.info.description}</span>
49
- <span className="theia-TreeNodeInfo theia-access-type" >{this.getAccessType()}</span>
50
- </span>;
48
+ return <>
49
+ <span className="line-info theia-data-breakpoint" title={this.origin.info.description}>
50
+ <span className="name">{this.origin.info.description}</span>
51
+ <span className="theia-TreeNodeInfo theia-access-type" >{this.getAccessType()}</span>
52
+ </span>
53
+ {this.renderActions()}
54
+ </>;
51
55
  }
52
56
 
57
+ protected renderActions(): React.ReactNode {
58
+ return <div className='theia-debug-breakpoint-actions'>
59
+ <div className={codicon('close', true)} title={nls.localizeByDefault('Remove Breakpoint')} onClick={this.onRemove} />
60
+ </div>;
61
+ }
62
+
63
+ protected onRemove = async () => {
64
+ await this.selectInTree();
65
+ this.remove();
66
+ };
67
+
53
68
  protected getAccessType(): string {
54
69
  switch (this.origin.raw.accessType) {
55
70
  case 'read': return 'Read';
@@ -62,7 +77,7 @@ export class DebugDataBreakpoint extends DebugBreakpoint<DataBreakpoint> {
62
77
  if (!this.isSupported()) {
63
78
  return {
64
79
  className: 'codicon-debug-breakpoint-unsupported',
65
- message: message ?? [nls.localize('theia/debug/data-breakpoint', 'Data Breakpoint')],
80
+ message: message ?? [nls.localizeByDefault('Data Breakpoint')],
66
81
  };
67
82
  }
68
83
  if (this.origin.raw.condition || this.origin.raw.hitCondition) {
@@ -73,7 +88,7 @@ export class DebugDataBreakpoint extends DebugBreakpoint<DataBreakpoint> {
73
88
  }
74
89
  return {
75
90
  className: 'codicon-debug-breakpoint-data',
76
- message: message || [nls.localize('theia/debug/data-breakpoint', 'Data Breakpoint')]
91
+ message: message || [nls.localizeByDefault('Data Breakpoint')]
77
92
  };
78
93
  }
79
94
  }
@@ -21,6 +21,7 @@ import { BreakpointManager } from '../breakpoint/breakpoint-manager';
21
21
  import { DebugBreakpoint, DebugBreakpointOptions, DebugBreakpointDecoration } from './debug-breakpoint';
22
22
  import { SingleTextInputDialog } from '@theia/core/lib/browser/dialogs';
23
23
  import { nls } from '@theia/core';
24
+ import { codicon } from '@theia/core/lib/browser';
24
25
 
25
26
  export class DebugFunctionBreakpoint extends DebugBreakpoint<FunctionBreakpoint> implements TreeElement {
26
27
 
@@ -59,9 +60,29 @@ export class DebugFunctionBreakpoint extends DebugBreakpoint<FunctionBreakpoint>
59
60
  }
60
61
 
61
62
  protected doRender(): React.ReactNode {
62
- return <span className='line-info'>{this.name}</span>;
63
+ return <React.Fragment>
64
+ <span className='line-info'>{this.name}</span>
65
+ {this.renderActions()}
66
+ </React.Fragment>;
63
67
  }
64
68
 
69
+ protected renderActions(): React.ReactNode {
70
+ return <div className='theia-debug-breakpoint-actions'>
71
+ <div className={codicon('edit', true)} title={nls.localizeByDefault('Edit Condition...')} onClick={this.onEdit} />
72
+ <div className={codicon('close', true)} title={nls.localizeByDefault('Remove Breakpoint')} onClick={this.onRemove} />
73
+ </div>;
74
+ }
75
+
76
+ protected onEdit = async () => {
77
+ await this.selectInTree();
78
+ this.open();
79
+ };
80
+
81
+ protected onRemove = async () => {
82
+ await this.selectInTree();
83
+ this.remove();
84
+ };
85
+
65
86
  protected override doGetDecoration(): DebugBreakpointDecoration {
66
87
  if (!this.isSupported()) {
67
88
  return this.getDisabledBreakpointDecoration(nls.localizeByDefault('Function breakpoints are not supported by this debug type'));
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { nls } from '@theia/core';
18
18
  import * as React from '@theia/core/shared/react';
19
+ import { codicon } from '@theia/core/lib/browser';
19
20
  import { BreakpointManager } from '../breakpoint/breakpoint-manager';
20
21
  import { InstructionBreakpoint } from '../breakpoint/breakpoint-marker';
21
22
  import { DebugBreakpoint, DebugBreakpointDecoration, DebugBreakpointOptions } from './debug-breakpoint';
@@ -44,9 +45,23 @@ export class DebugInstructionBreakpoint extends DebugBreakpoint<InstructionBreak
44
45
  }
45
46
 
46
47
  protected doRender(): React.ReactNode {
47
- return <span className="line-info">{this.origin.instructionReference}</span>;
48
+ return <React.Fragment>
49
+ <span className="line-info">{this.origin.instructionReference}</span>;
50
+ {this.renderActions()}
51
+ </React.Fragment>;
48
52
  }
49
53
 
54
+ protected renderActions(): React.ReactNode {
55
+ return <div className='theia-debug-breakpoint-actions'>
56
+ <div className={codicon('close', true)} title={nls.localizeByDefault('Remove Breakpoint')} onClick={this.onRemove} />
57
+ </div>;
58
+ }
59
+
60
+ protected onRemove = async () => {
61
+ await this.selectInTree();
62
+ this.remove();
63
+ };
64
+
50
65
  protected getBreakpointDecoration(message?: string[]): DebugBreakpointDecoration {
51
66
  if (!this.isSupported()) {
52
67
  return {
@@ -16,14 +16,15 @@
16
16
 
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol';
19
- import { nls, RecursivePartial } from '@theia/core';
19
+ import { CommandService, nls, RecursivePartial } from '@theia/core';
20
20
  import URI from '@theia/core/lib/common/uri';
21
21
  import { EditorWidget, Range } from '@theia/editor/lib/browser';
22
- import { TREE_NODE_INFO_CLASS, WidgetOpenerOptions } from '@theia/core/lib/browser';
22
+ import { TREE_NODE_INFO_CLASS, WidgetOpenerOptions, codicon } from '@theia/core/lib/browser';
23
23
  import { TreeElement } from '@theia/core/lib/browser/source-tree';
24
24
  import { SourceBreakpoint } from '../breakpoint/breakpoint-marker';
25
25
  import { DebugSource } from './debug-source';
26
26
  import { DebugBreakpoint, DebugBreakpointOptions, DebugBreakpointData, DebugBreakpointDecoration } from './debug-breakpoint';
27
+ import { DebugCommands } from '../debug-commands';
27
28
 
28
29
  export class DebugSourceBreakpointData extends DebugBreakpointData {
29
30
  readonly origins: SourceBreakpoint[];
@@ -31,11 +32,13 @@ export class DebugSourceBreakpointData extends DebugBreakpointData {
31
32
 
32
33
  export class DebugSourceBreakpoint extends DebugBreakpoint<SourceBreakpoint> implements TreeElement {
33
34
 
35
+ protected readonly commandService: CommandService;
34
36
  readonly origins: SourceBreakpoint[];
35
37
 
36
- constructor(origin: SourceBreakpoint, options: DebugBreakpointOptions) {
38
+ constructor(origin: SourceBreakpoint, options: DebugBreakpointOptions, commandService: CommandService) {
37
39
  super(new URI(origin.uri), options);
38
40
  this.origins = [origin];
41
+ this.commandService = commandService;
39
42
  }
40
43
 
41
44
  override update(data: Partial<DebugSourceBreakpointData>): void {
@@ -150,10 +153,28 @@ export class DebugSourceBreakpoint extends DebugBreakpoint<SourceBreakpoint> imp
150
153
  <span className='name'>{this.labelProvider.getName(this.uri)} </span>
151
154
  <span className={'path ' + TREE_NODE_INFO_CLASS}>{this.labelProvider.getLongName(this.uri.parent)} </span>
152
155
  </span>
156
+ {this.renderActions()}
153
157
  <span className='line'>{this.renderPosition()}</span>
154
158
  </React.Fragment>;
155
159
  }
156
160
 
161
+ protected renderActions(): React.ReactNode {
162
+ return <div className='theia-debug-breakpoint-actions'>
163
+ <div className={codicon('edit', true)} title={nls.localizeByDefault('Edit Breakpoint')} onClick={this.onEdit} />
164
+ <div className={codicon('close', true)} title={nls.localizeByDefault('Remove Breakpoint')} onClick={this.onRemove} />
165
+ </div>;
166
+ }
167
+
168
+ protected onEdit = async () => {
169
+ await this.selectInTree();
170
+ this.commandService.executeCommand(DebugCommands.EDIT_BREAKPOINT.id, this);
171
+ };
172
+
173
+ protected onRemove = async () => {
174
+ await this.selectInTree();
175
+ this.remove();
176
+ };
177
+
157
178
  renderPosition(): string {
158
179
  return this.line + (typeof this.column === 'number' ? ':' + this.column : '');
159
180
  }
@@ -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