@theia/debug 1.70.0-next.81 → 1.71.0-next.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.
- package/lib/browser/breakpoint/breakpoint-manager.d.ts +80 -45
- package/lib/browser/breakpoint/breakpoint-manager.d.ts.map +1 -1
- package/lib/browser/breakpoint/breakpoint-manager.js +553 -170
- package/lib/browser/breakpoint/breakpoint-manager.js.map +1 -1
- package/lib/browser/breakpoint/breakpoint-manager.spec.d.ts +2 -0
- package/lib/browser/breakpoint/breakpoint-manager.spec.d.ts.map +1 -0
- package/lib/browser/breakpoint/breakpoint-manager.spec.js +861 -0
- package/lib/browser/breakpoint/breakpoint-manager.spec.js.map +1 -0
- package/lib/browser/breakpoint/breakpoint-marker.d.ts +7 -10
- package/lib/browser/breakpoint/breakpoint-marker.d.ts.map +1 -1
- package/lib/browser/breakpoint/breakpoint-marker.js +14 -11
- package/lib/browser/breakpoint/breakpoint-marker.js.map +1 -1
- package/lib/browser/breakpoint/debug-data-breakpoint-actions.js +1 -1
- package/lib/browser/breakpoint/debug-data-breakpoint-actions.js.map +1 -1
- package/lib/browser/debug-frontend-application-contribution.d.ts +1 -2
- package/lib/browser/debug-frontend-application-contribution.d.ts.map +1 -1
- package/lib/browser/debug-frontend-application-contribution.js +13 -21
- package/lib/browser/debug-frontend-application-contribution.js.map +1 -1
- package/lib/browser/debug-frontend-module.d.ts.map +1 -1
- package/lib/browser/debug-frontend-module.js +3 -0
- package/lib/browser/debug-frontend-module.js.map +1 -1
- package/lib/browser/debug-session-manager.d.ts +8 -27
- package/lib/browser/debug-session-manager.d.ts.map +1 -1
- package/lib/browser/debug-session-manager.js +14 -132
- package/lib/browser/debug-session-manager.js.map +1 -1
- package/lib/browser/debug-session.d.ts +1 -21
- package/lib/browser/debug-session.d.ts.map +1 -1
- package/lib/browser/debug-session.js +72 -203
- package/lib/browser/debug-session.js.map +1 -1
- package/lib/browser/disassembly-view/disassembly-view-breakpoint-renderer.js +1 -1
- package/lib/browser/disassembly-view/disassembly-view-breakpoint-renderer.js.map +1 -1
- package/lib/browser/disassembly-view/disassembly-view-widget.d.ts.map +1 -1
- package/lib/browser/disassembly-view/disassembly-view-widget.js +17 -24
- package/lib/browser/disassembly-view/disassembly-view-widget.js.map +1 -1
- package/lib/browser/editor/debug-editor-model.d.ts +15 -5
- package/lib/browser/editor/debug-editor-model.d.ts.map +1 -1
- package/lib/browser/editor/debug-editor-model.js +56 -32
- package/lib/browser/editor/debug-editor-model.js.map +1 -1
- package/lib/browser/model/debug-breakpoint-opener.d.ts +14 -0
- package/lib/browser/model/debug-breakpoint-opener.d.ts.map +1 -0
- package/lib/browser/model/debug-breakpoint-opener.js +67 -0
- package/lib/browser/model/debug-breakpoint-opener.js.map +1 -0
- package/lib/browser/model/debug-breakpoint.d.ts +32 -13
- package/lib/browser/model/debug-breakpoint.d.ts.map +1 -1
- package/lib/browser/model/debug-breakpoint.js +76 -16
- package/lib/browser/model/debug-breakpoint.js.map +1 -1
- package/lib/browser/model/debug-data-breakpoint.d.ts +1 -0
- package/lib/browser/model/debug-data-breakpoint.d.ts.map +1 -1
- package/lib/browser/model/debug-data-breakpoint.js +6 -5
- package/lib/browser/model/debug-data-breakpoint.js.map +1 -1
- package/lib/browser/model/debug-function-breakpoint.d.ts +4 -1
- package/lib/browser/model/debug-function-breakpoint.d.ts.map +1 -1
- package/lib/browser/model/debug-function-breakpoint.js +20 -29
- package/lib/browser/model/debug-function-breakpoint.js.map +1 -1
- package/lib/browser/model/debug-instruction-breakpoint.d.ts +2 -1
- package/lib/browser/model/debug-instruction-breakpoint.d.ts.map +1 -1
- package/lib/browser/model/debug-instruction-breakpoint.js +8 -8
- package/lib/browser/model/debug-instruction-breakpoint.js.map +1 -1
- package/lib/browser/model/debug-source-breakpoint.d.ts +6 -15
- package/lib/browser/model/debug-source-breakpoint.d.ts.map +1 -1
- package/lib/browser/model/debug-source-breakpoint.js +16 -90
- package/lib/browser/model/debug-source-breakpoint.js.map +1 -1
- package/lib/browser/view/debug-breakpoints-source.d.ts +0 -2
- package/lib/browser/view/debug-breakpoints-source.d.ts.map +1 -1
- package/lib/browser/view/debug-breakpoints-source.js +2 -10
- package/lib/browser/view/debug-breakpoints-source.js.map +1 -1
- package/lib/browser/view/debug-breakpoints-widget.d.ts +2 -0
- package/lib/browser/view/debug-breakpoints-widget.d.ts.map +1 -1
- package/lib/browser/view/debug-breakpoints-widget.js +3 -0
- package/lib/browser/view/debug-breakpoints-widget.js.map +1 -1
- package/lib/browser/view/debug-exception-breakpoint.d.ts +18 -11
- package/lib/browser/view/debug-exception-breakpoint.d.ts.map +1 -1
- package/lib/browser/view/debug-exception-breakpoint.js +58 -24
- package/lib/browser/view/debug-exception-breakpoint.js.map +1 -1
- package/lib/browser/view/debug-view-model.d.ts +8 -4
- package/lib/browser/view/debug-view-model.d.ts.map +1 -1
- package/lib/browser/view/debug-view-model.js +16 -9
- package/lib/browser/view/debug-view-model.js.map +1 -1
- package/package.json +16 -16
- package/src/browser/breakpoint/breakpoint-manager.spec.ts +1106 -0
- package/src/browser/breakpoint/breakpoint-manager.ts +583 -194
- package/src/browser/breakpoint/breakpoint-marker.ts +21 -15
- package/src/browser/breakpoint/debug-data-breakpoint-actions.ts +1 -1
- package/src/browser/debug-frontend-application-contribution.ts +18 -23
- package/src/browser/debug-frontend-module.ts +5 -1
- package/src/browser/debug-session-manager.ts +15 -147
- package/src/browser/debug-session.tsx +71 -221
- package/src/browser/disassembly-view/disassembly-view-breakpoint-renderer.ts +1 -1
- package/src/browser/disassembly-view/disassembly-view-widget.ts +17 -23
- package/src/browser/editor/debug-editor-model.ts +58 -35
- package/src/browser/model/debug-breakpoint-opener.ts +51 -0
- package/src/browser/model/debug-breakpoint.tsx +101 -20
- package/src/browser/model/debug-data-breakpoint.tsx +8 -5
- package/src/browser/model/debug-function-breakpoint.tsx +18 -29
- package/src/browser/model/debug-instruction-breakpoint.tsx +10 -8
- package/src/browser/model/debug-source-breakpoint.tsx +23 -101
- package/src/browser/view/debug-breakpoints-source.tsx +2 -9
- package/src/browser/view/debug-breakpoints-widget.ts +6 -0
- package/src/browser/view/debug-exception-breakpoint.tsx +66 -27
- package/src/browser/view/debug-view-model.ts +21 -13
|
@@ -14,29 +14,35 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { StorageService } from '@theia/core/lib/browser';
|
|
21
|
-
import { Marker } from '@theia/markers/lib/common/marker';
|
|
22
|
-
import { MarkerManager } from '@theia/markers/lib/browser/marker-manager';
|
|
17
|
+
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
18
|
+
import { CommandService, Emitter, Event, MapUtils } from '@theia/core/lib/common';
|
|
19
|
+
import { LabelProvider, OpenerService, StorageService } from '@theia/core/lib/browser';
|
|
23
20
|
import URI from '@theia/core/lib/common/uri';
|
|
24
|
-
import { SourceBreakpoint,
|
|
21
|
+
import { SourceBreakpoint, ExceptionBreakpoint, FunctionBreakpoint, BaseBreakpoint, InstructionBreakpoint, DataBreakpoint } from './breakpoint-marker';
|
|
22
|
+
import { DebugSourceBreakpoint } from '../model/debug-source-breakpoint';
|
|
23
|
+
import { DebugFunctionBreakpoint } from '../model/debug-function-breakpoint';
|
|
24
|
+
import { DebugInstructionBreakpoint } from '../model/debug-instruction-breakpoint';
|
|
25
|
+
import { DebugExceptionBreakpoint } from '../view/debug-exception-breakpoint';
|
|
26
|
+
import { DebugDataBreakpoint } from '../model/debug-data-breakpoint';
|
|
27
|
+
import { BPCapabilities, DebugBreakpoint, DebugBreakpointOptions } from '../model/debug-breakpoint';
|
|
25
28
|
import { DebugProtocol } from '@vscode/debugprotocol';
|
|
29
|
+
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
30
|
+
import { FileChangeType } from '@theia/filesystem/lib/common/files';
|
|
26
31
|
|
|
27
|
-
export interface BreakpointsChangeEvent<T extends
|
|
32
|
+
export interface BreakpointsChangeEvent<T extends object> {
|
|
28
33
|
uri: URI
|
|
29
34
|
added: T[]
|
|
30
35
|
removed: T[]
|
|
31
36
|
changed: T[]
|
|
32
37
|
}
|
|
33
|
-
|
|
34
|
-
export type
|
|
35
|
-
export type
|
|
36
|
-
export type
|
|
38
|
+
|
|
39
|
+
export type SourceBreakpointsChangeEvent = BreakpointsChangeEvent<DebugSourceBreakpoint>;
|
|
40
|
+
export type FunctionBreakpointsChangeEvent = BreakpointsChangeEvent<DebugFunctionBreakpoint>;
|
|
41
|
+
export type InstructionBreakpointsChangeEvent = BreakpointsChangeEvent<DebugInstructionBreakpoint>;
|
|
42
|
+
export type DataBreakpointsChangeEvent = BreakpointsChangeEvent<DebugDataBreakpoint>;
|
|
37
43
|
|
|
38
44
|
@injectable()
|
|
39
|
-
export class BreakpointManager
|
|
45
|
+
export class BreakpointManager {
|
|
40
46
|
|
|
41
47
|
static EXCEPTION_URI = new URI('debug:exception://');
|
|
42
48
|
|
|
@@ -46,13 +52,31 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
46
52
|
|
|
47
53
|
static DATA_URI = new URI('debug:data://');
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
// ── Source breakpoints, keyed by URI string ──
|
|
56
|
+
|
|
57
|
+
protected readonly sourceBreakpoints = new Map<string, DebugSourceBreakpoint[]>();
|
|
58
|
+
|
|
59
|
+
// ── Injected services ──
|
|
50
60
|
|
|
51
61
|
@inject(StorageService)
|
|
52
62
|
protected readonly storage: StorageService;
|
|
63
|
+
@inject(LabelProvider)
|
|
64
|
+
protected readonly labelProvider: LabelProvider;
|
|
65
|
+
@inject(OpenerService)
|
|
66
|
+
protected readonly openerService: OpenerService;
|
|
67
|
+
@inject(CommandService)
|
|
68
|
+
protected readonly commandService: CommandService;
|
|
69
|
+
@inject(FileService)
|
|
70
|
+
protected readonly fileService: FileService;
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
// ── Events ──
|
|
73
|
+
|
|
74
|
+
protected readonly onDidChangeMarkersEmitter = new Emitter<URI>();
|
|
75
|
+
get onDidChangeMarkers(): Event<URI> {
|
|
76
|
+
return this.onDidChangeMarkersEmitter.event;
|
|
77
|
+
}
|
|
78
|
+
protected fireOnDidChangeMarkers(uri: URI): void {
|
|
79
|
+
this.onDidChangeMarkersEmitter.fire(uri);
|
|
56
80
|
}
|
|
57
81
|
|
|
58
82
|
protected readonly onDidChangeBreakpointsEmitter = new Emitter<SourceBreakpointsChangeEvent>();
|
|
@@ -67,174 +91,482 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
67
91
|
protected readonly onDidChangeDataBreakpointsEmitter = new Emitter<DataBreakpointsChangeEvent>();
|
|
68
92
|
readonly onDidChangeDataBreakpoints = this.onDidChangeDataBreakpointsEmitter.event;
|
|
69
93
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
// ── Cached options object ──
|
|
95
|
+
|
|
96
|
+
protected _breakpointOptions: DebugBreakpointOptions | undefined;
|
|
97
|
+
|
|
98
|
+
// ── Initialization ──
|
|
99
|
+
|
|
100
|
+
@postConstruct()
|
|
101
|
+
protected init(): void {
|
|
102
|
+
this.fileService.onDidFilesChange(event => {
|
|
103
|
+
if (event.gotDeleted()) {
|
|
104
|
+
for (const uriString of this.sourceBreakpoints.keys()) {
|
|
105
|
+
const uri = new URI(uriString);
|
|
106
|
+
if (event.contains(uri, FileChangeType.DELETED)) {
|
|
107
|
+
this.sourceBreakpoints.delete(uriString);
|
|
108
|
+
this.fireOnDidChangeMarkers(uri);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Source breakpoint storage ──
|
|
116
|
+
|
|
117
|
+
getBreakpoints(uri?: URI): readonly DebugSourceBreakpoint[] {
|
|
118
|
+
if (uri) {
|
|
119
|
+
return this.sourceBreakpoints.get(uri.toString()) ?? [];
|
|
120
|
+
}
|
|
121
|
+
const result: DebugSourceBreakpoint[] = [];
|
|
122
|
+
for (const bps of this.sourceBreakpoints.values()) {
|
|
123
|
+
result.push(...bps);
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getUris(): IterableIterator<string> {
|
|
129
|
+
return this.sourceBreakpoints.keys();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
hasBreakpoints(): boolean {
|
|
133
|
+
return this.sourceBreakpoints.size > 0 || this.functionBreakpoints.length > 0 || this.instructionBreakpoints.length > 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Replace the source breakpoints for a URI. Incoming `breakpoints` are
|
|
138
|
+
* plain `SourceBreakpoint` data; existing `DebugSourceBreakpoint` wrappers
|
|
139
|
+
* are preserved by ID so that session data survives position changes.
|
|
140
|
+
*/
|
|
141
|
+
setBreakpoints(uri: URI, breakpoints: SourceBreakpoint[]): void {
|
|
142
|
+
const current = this.getBreakpoints(uri);
|
|
143
|
+
const currentById = new Map(current.map(bp => [bp.id, bp]));
|
|
144
|
+
|
|
145
|
+
const sorted = breakpoints
|
|
146
|
+
.slice()
|
|
147
|
+
.sort((a, b) => (a.raw.line - b.raw.line) || ((a.raw.column || 0) - (b.raw.column || 0)));
|
|
148
|
+
|
|
149
|
+
const seen = new Set<string>();
|
|
150
|
+
const newBps: DebugSourceBreakpoint[] = [];
|
|
151
|
+
for (const bp of sorted) {
|
|
152
|
+
const posKey = `${bp.raw.line}:${bp.raw.column ?? 0}`;
|
|
153
|
+
if (seen.has(posKey)) { continue; }
|
|
154
|
+
seen.add(posKey);
|
|
155
|
+
|
|
156
|
+
// Prefer matching by ID (preserves identity across position changes).
|
|
157
|
+
const existing = currentById.get(bp.id);
|
|
158
|
+
if (existing) {
|
|
159
|
+
existing.origin.raw = bp.raw;
|
|
160
|
+
existing.origin.enabled = bp.enabled;
|
|
161
|
+
newBps.push(existing);
|
|
83
162
|
} else {
|
|
84
|
-
|
|
85
|
-
// We also fire an event if oldMarker === newMarker, as we cannot actually detect a change in this case
|
|
86
|
-
// (https://github.com/eclipse-theia/theia/issues/12546).
|
|
87
|
-
didChangeMarkers ||= !!added.length || oldMarker === newMarker || !deepEqual(oldMarker, newMarker);
|
|
88
|
-
changed.push(newMarker);
|
|
163
|
+
newBps.push(this.toDebugSourceBreakpoint(bp));
|
|
89
164
|
}
|
|
90
165
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
166
|
+
|
|
167
|
+
this.applySourceBreakpoints(uri, newBps, current);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
addBreakpoint(breakpoint: SourceBreakpoint): DebugSourceBreakpoint {
|
|
171
|
+
const uri = new URI(breakpoint.uri);
|
|
172
|
+
const current = this.getBreakpoints(uri);
|
|
173
|
+
|
|
174
|
+
// Check for positional duplicate.
|
|
175
|
+
const duplicate = current.find(
|
|
176
|
+
c => c.line === breakpoint.raw.line && c.column === breakpoint.raw.column
|
|
177
|
+
);
|
|
178
|
+
if (duplicate) { return duplicate; }
|
|
179
|
+
|
|
180
|
+
const bp = this.toDebugSourceBreakpoint(breakpoint);
|
|
181
|
+
const newBps = [...current, bp];
|
|
182
|
+
this.applySourceBreakpoints(uri, newBps, current);
|
|
183
|
+
return bp;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
removeBreakpoint(breakpoint: DebugSourceBreakpoint): void {
|
|
187
|
+
const current = this.getBreakpoints(breakpoint.uri);
|
|
188
|
+
const index = current.indexOf(breakpoint);
|
|
189
|
+
if (index === -1) { return; }
|
|
190
|
+
const retained = [...current.slice(0, index), ...current.slice(index + 1)];
|
|
191
|
+
this.applySourceBreakpoints(breakpoint.uri, retained, current);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Diff `oldBps` → `newBps`, store, fire markers and typed events.
|
|
196
|
+
* Both arrays must be for the same URI.
|
|
197
|
+
*/
|
|
198
|
+
protected applySourceBreakpoints(uri: URI, newBps: readonly DebugSourceBreakpoint[], oldBps: readonly DebugSourceBreakpoint[]): void {
|
|
199
|
+
const oldById = new Map(oldBps.map(bp => [bp.id, bp]));
|
|
200
|
+
const added: DebugSourceBreakpoint[] = [];
|
|
201
|
+
const changed: DebugSourceBreakpoint[] = [];
|
|
202
|
+
let didChange = false;
|
|
203
|
+
for (const bp of newBps) {
|
|
204
|
+
const old = oldById.get(bp.id);
|
|
205
|
+
if (!old) {
|
|
206
|
+
added.push(bp);
|
|
207
|
+
didChange = true;
|
|
208
|
+
} else {
|
|
209
|
+
changed.push(bp);
|
|
210
|
+
// Identity match: the wrapper was mutated in place, or the same
|
|
211
|
+
// object was passed back (editor model position update).
|
|
212
|
+
// Either way we must re-fire to keep decorations in sync.
|
|
213
|
+
didChange = true;
|
|
214
|
+
oldById.delete(bp.id);
|
|
94
215
|
}
|
|
95
216
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
217
|
+
const removed = Array.from(oldById.values());
|
|
218
|
+
didChange ||= removed.length > 0;
|
|
219
|
+
|
|
220
|
+
if (!didChange) { return; }
|
|
221
|
+
|
|
222
|
+
if (newBps.length > 0) {
|
|
223
|
+
this.sourceBreakpoints.set(uri.toString(), [...newBps]);
|
|
224
|
+
} else {
|
|
225
|
+
this.sourceBreakpoints.delete(uri.toString());
|
|
99
226
|
}
|
|
100
|
-
|
|
227
|
+
this.fireOnDidChangeMarkers(uri);
|
|
228
|
+
this.onDidChangeBreakpointsEmitter.fire({ uri, added, removed, changed });
|
|
101
229
|
}
|
|
102
230
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
uri
|
|
106
|
-
|
|
107
|
-
|
|
231
|
+
removeBreakpoints(): void {
|
|
232
|
+
for (const uriString of [...this.sourceBreakpoints.keys()]) {
|
|
233
|
+
const uri = new URI(uriString);
|
|
234
|
+
const old = this.sourceBreakpoints.get(uriString) ?? [];
|
|
235
|
+
this.sourceBreakpoints.delete(uriString);
|
|
236
|
+
this.fireOnDidChangeMarkers(uri);
|
|
237
|
+
if (old.length) {
|
|
238
|
+
this.onDidChangeBreakpointsEmitter.fire({ uri, added: [], removed: old, changed: [] });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.setFunctionBreakpoints([]);
|
|
242
|
+
this.setInstructionBreakpoints([]);
|
|
243
|
+
this.setDataBreakpoints([]);
|
|
108
244
|
}
|
|
109
245
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})[0];
|
|
115
|
-
return marker && marker.data;
|
|
246
|
+
// ── Query helpers ──
|
|
247
|
+
|
|
248
|
+
getLineBreakpoints(uri: URI, line: number): DebugSourceBreakpoint[] {
|
|
249
|
+
return this.getBreakpoints(uri).filter(bp => bp.line === line);
|
|
116
250
|
}
|
|
117
251
|
|
|
118
|
-
|
|
119
|
-
return this.
|
|
252
|
+
getInlineBreakpoint(uri: URI, line: number, column: number): DebugSourceBreakpoint | undefined {
|
|
253
|
+
return this.getBreakpoints(uri).find(bp => bp.line === line && bp.column === column);
|
|
120
254
|
}
|
|
121
255
|
|
|
122
|
-
|
|
123
|
-
|
|
256
|
+
getBreakpointById(id: string): DebugBreakpoint | undefined {
|
|
257
|
+
for (const bp of this.allBreakpoints()) {
|
|
258
|
+
if (bp.id === id) { return bp; }
|
|
259
|
+
}
|
|
124
260
|
}
|
|
125
261
|
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
262
|
+
* allBreakpoints(): IterableIterator<DebugBreakpoint> {
|
|
263
|
+
for (const bps of this.sourceBreakpoints.values()) {
|
|
264
|
+
yield* bps;
|
|
265
|
+
}
|
|
266
|
+
yield* this.functionBreakpoints;
|
|
267
|
+
yield* this.instructionBreakpoints;
|
|
268
|
+
yield* this.exceptionBreakpoints;
|
|
269
|
+
yield* this.dataBreakpoints;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ── Session data ──
|
|
273
|
+
|
|
274
|
+
updateSessionData(sessionId: string, sessionCapabilities: DebugProtocol.Capabilities, bps?: Map<string, DebugProtocol.Breakpoint>): void {
|
|
275
|
+
const bpCapabilities = this.toBpCapabilities(sessionCapabilities);
|
|
276
|
+
const updatedUris = new Map<string, DebugBreakpoint[]>();
|
|
277
|
+
for (const bp of this.allBreakpoints()) {
|
|
278
|
+
if (!bps) {
|
|
279
|
+
if (bp.update(sessionId, undefined)) {
|
|
280
|
+
MapUtils.addOrInsertWith(updatedUris, bp.uri.toString(), bp);
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
const dataForBp = bps.get(bp.id);
|
|
284
|
+
if (!dataForBp) { continue; }
|
|
285
|
+
bp.update(sessionId, { ...bpCapabilities, ...dataForBp });
|
|
286
|
+
MapUtils.addOrInsertWith(updatedUris, bp.uri.toString(), bp);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const changed of updatedUris.values()) {
|
|
290
|
+
this.fireTypedBreakpointEvent(changed[0].uri, [], changed, []);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected toBpCapabilities(capabilities: DebugProtocol.Capabilities): BPCapabilities {
|
|
295
|
+
return {
|
|
296
|
+
supportsConditionalBreakpoints: !!capabilities.supportsConditionalBreakpoints,
|
|
297
|
+
supportsHitConditionalBreakpoints: !!capabilities.supportsHitConditionalBreakpoints,
|
|
298
|
+
supportsLogPoints: !!capabilities.supportsLogPoints,
|
|
299
|
+
supportsFunctionBreakpoints: !!capabilities.supportsFunctionBreakpoints,
|
|
300
|
+
supportsDataBreakpoints: !!capabilities.supportsDataBreakpoints,
|
|
301
|
+
supportsInstructionBreakpoints: !!capabilities.supportsInstructionBreakpoints,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── Breakpoint construction ──
|
|
306
|
+
|
|
307
|
+
protected toDebugSourceBreakpoint(source: SourceBreakpoint): DebugSourceBreakpoint {
|
|
308
|
+
return DebugSourceBreakpoint.create(source, this.getBreakpointOptions());
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getBreakpointOptions(): DebugBreakpointOptions {
|
|
312
|
+
if (!this._breakpointOptions) {
|
|
313
|
+
this._breakpointOptions = {
|
|
314
|
+
labelProvider: this.labelProvider,
|
|
315
|
+
openerService: this.openerService,
|
|
316
|
+
commandService: this.commandService,
|
|
317
|
+
breakpoints: this
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return this._breakpointOptions;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ── Enable / disable ──
|
|
324
|
+
|
|
325
|
+
protected _breakpointsEnabled = true;
|
|
326
|
+
|
|
327
|
+
get breakpointsEnabled(): boolean {
|
|
328
|
+
return this._breakpointsEnabled;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
set breakpointsEnabled(breakpointsEnabled: boolean) {
|
|
332
|
+
if (this._breakpointsEnabled !== breakpointsEnabled) {
|
|
333
|
+
this._breakpointsEnabled = breakpointsEnabled;
|
|
334
|
+
for (const uri of this.getUris()) {
|
|
335
|
+
this.fireOnDidChangeMarkers(new URI(uri));
|
|
336
|
+
}
|
|
337
|
+
this.fireOnDidChangeMarkers(BreakpointManager.FUNCTION_URI);
|
|
338
|
+
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
339
|
+
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
340
|
+
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
|
|
134
341
|
}
|
|
135
|
-
return false;
|
|
136
342
|
}
|
|
137
343
|
|
|
138
344
|
enableAllBreakpoints(enabled: boolean): void {
|
|
139
345
|
for (const uriString of this.getUris()) {
|
|
140
346
|
let didChange = false;
|
|
141
347
|
const uri = new URI(uriString);
|
|
142
|
-
const
|
|
143
|
-
for (const
|
|
144
|
-
|
|
145
|
-
marker.data.enabled = enabled;
|
|
146
|
-
didChange = true;
|
|
147
|
-
}
|
|
348
|
+
const bps = this.getBreakpoints(uri);
|
|
349
|
+
for (const bp of bps) {
|
|
350
|
+
didChange ||= this.doEnableBreakpoint(bp, enabled);
|
|
148
351
|
}
|
|
149
352
|
if (didChange) {
|
|
150
353
|
this.fireOnDidChangeMarkers(uri);
|
|
354
|
+
this.onDidChangeBreakpointsEmitter.fire({ uri, added: [], removed: [], changed: [...bps] });
|
|
151
355
|
}
|
|
152
356
|
}
|
|
153
357
|
let didChangeFunction = false;
|
|
154
|
-
for (const breakpoint of
|
|
155
|
-
if (breakpoint.enabled !== enabled) {
|
|
156
|
-
breakpoint.enabled = enabled;
|
|
358
|
+
for (const breakpoint of this.functionBreakpoints) {
|
|
359
|
+
if (breakpoint.origin.enabled !== enabled) {
|
|
360
|
+
breakpoint.origin.enabled = enabled;
|
|
157
361
|
didChangeFunction = true;
|
|
158
|
-
|
|
159
362
|
}
|
|
160
363
|
}
|
|
161
364
|
if (didChangeFunction) {
|
|
162
365
|
this.fireOnDidChangeMarkers(BreakpointManager.FUNCTION_URI);
|
|
366
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({
|
|
367
|
+
uri: BreakpointManager.FUNCTION_URI, added: [], removed: [], changed: [...this.functionBreakpoints]
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
let didChangeInstruction = false;
|
|
371
|
+
for (const breakpoint of this.instructionBreakpoints) {
|
|
372
|
+
if (breakpoint.origin.enabled !== enabled) {
|
|
373
|
+
breakpoint.origin.enabled = enabled;
|
|
374
|
+
didChangeInstruction = true;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (didChangeInstruction) {
|
|
378
|
+
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
379
|
+
this.onDidChangeInstructionBreakpointsEmitter.fire({
|
|
380
|
+
uri: BreakpointManager.INSTRUCTION_URI, added: [], removed: [], changed: [...this.instructionBreakpoints]
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
let didChangeData = false;
|
|
384
|
+
for (const breakpoint of this.dataBreakpoints) {
|
|
385
|
+
if (breakpoint.origin.enabled !== enabled) {
|
|
386
|
+
breakpoint.origin.enabled = enabled;
|
|
387
|
+
didChangeData = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (didChangeData) {
|
|
391
|
+
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
392
|
+
this.onDidChangeDataBreakpointsEmitter.fire({
|
|
393
|
+
uri: BreakpointManager.DATA_URI, added: [], removed: [], changed: [...this.dataBreakpoints]
|
|
394
|
+
});
|
|
163
395
|
}
|
|
164
396
|
}
|
|
165
397
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
398
|
+
enableBreakpoint<T extends DebugBreakpoint>(breakpoint: T, enabled: boolean): void {
|
|
399
|
+
const didChange = this.doEnableBreakpoint(breakpoint, enabled);
|
|
400
|
+
if (didChange) {
|
|
401
|
+
this.fireBreakpointChanged(breakpoint);
|
|
402
|
+
}
|
|
169
403
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
this.fireOnDidChangeMarkers(BreakpointManager.FUNCTION_URI);
|
|
404
|
+
|
|
405
|
+
protected doEnableBreakpoint(breakpoint: DebugBreakpoint, enabled: boolean): boolean {
|
|
406
|
+
if (breakpoint.origin.enabled !== enabled) {
|
|
407
|
+
breakpoint.origin.enabled = enabled;
|
|
408
|
+
return true;
|
|
177
409
|
}
|
|
410
|
+
return false;
|
|
178
411
|
}
|
|
179
412
|
|
|
180
|
-
|
|
413
|
+
// ── Generic update / fire ──
|
|
181
414
|
|
|
182
|
-
|
|
183
|
-
|
|
415
|
+
updateBreakpoint<U extends BaseBreakpoint, T extends DebugBreakpoint<U>>(bp: T, update: Partial<U['raw']>): void {
|
|
416
|
+
bp.origin.raw = { ...bp.origin.raw, ...update };
|
|
417
|
+
this.fireBreakpointChanged(bp);
|
|
184
418
|
}
|
|
185
419
|
|
|
186
|
-
|
|
187
|
-
|
|
420
|
+
fireBreakpointChanged(breakpoint: DebugBreakpoint): void {
|
|
421
|
+
this.fireOnDidChangeMarkers(breakpoint.uri);
|
|
422
|
+
this.fireTypedBreakpointEvent(breakpoint.uri, [], [breakpoint], []);
|
|
188
423
|
}
|
|
189
424
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.
|
|
425
|
+
protected fireTypedBreakpointEvent(uri: URI, added: DebugBreakpoint[], changed: DebugBreakpoint[], removed: DebugBreakpoint[]): void {
|
|
426
|
+
// All breakpoints in a single call are the same type (grouped by URI).
|
|
427
|
+
const sample = added[0] ?? changed[0] ?? removed[0];
|
|
428
|
+
if (!sample) { return; }
|
|
429
|
+
if (sample instanceof DebugSourceBreakpoint) {
|
|
430
|
+
this.onDidChangeBreakpointsEmitter.fire({ uri, added, changed, removed } as SourceBreakpointsChangeEvent);
|
|
431
|
+
} else if (sample instanceof DebugFunctionBreakpoint) {
|
|
432
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri, added, changed, removed } as FunctionBreakpointsChangeEvent);
|
|
433
|
+
} else if (sample instanceof DebugInstructionBreakpoint) {
|
|
434
|
+
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri, added, changed, removed } as InstructionBreakpointsChangeEvent);
|
|
435
|
+
} else if (sample instanceof DebugDataBreakpoint) {
|
|
436
|
+
this.onDidChangeDataBreakpointsEmitter.fire({ uri, added, changed, removed } as DataBreakpointsChangeEvent);
|
|
196
437
|
}
|
|
197
|
-
|
|
198
|
-
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ── Bulk remove by ID (plugin API) ──
|
|
441
|
+
|
|
442
|
+
removeBreakpointsById(ids: string[]): void {
|
|
443
|
+
const toRemove = new Set(ids);
|
|
444
|
+
|
|
445
|
+
// Source breakpoints
|
|
446
|
+
for (const [uriString, bps] of this.sourceBreakpoints.entries()) {
|
|
447
|
+
const retained = bps.filter(bp => !toRemove.has(bp.id));
|
|
448
|
+
if (retained.length !== bps.length) {
|
|
449
|
+
const removed = bps.filter(bp => toRemove.has(bp.id));
|
|
450
|
+
const uri = new URI(uriString);
|
|
451
|
+
if (retained.length > 0) {
|
|
452
|
+
this.sourceBreakpoints.set(uriString, retained);
|
|
453
|
+
} else {
|
|
454
|
+
this.sourceBreakpoints.delete(uriString);
|
|
455
|
+
}
|
|
456
|
+
this.fireOnDidChangeMarkers(uri);
|
|
457
|
+
this.onDidChangeBreakpointsEmitter.fire({ uri, removed, added: [], changed: [] });
|
|
458
|
+
}
|
|
199
459
|
}
|
|
200
|
-
|
|
201
|
-
|
|
460
|
+
|
|
461
|
+
// Function breakpoints
|
|
462
|
+
const functionRemoved: DebugFunctionBreakpoint[] = [];
|
|
463
|
+
this.functionBreakpoints = this.functionBreakpoints.filter(bp => {
|
|
464
|
+
if (toRemove.has(bp.id)) {
|
|
465
|
+
functionRemoved.push(bp);
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
});
|
|
470
|
+
if (functionRemoved.length) {
|
|
471
|
+
this.fireOnDidChangeMarkers(BreakpointManager.FUNCTION_URI);
|
|
472
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri: BreakpointManager.FUNCTION_URI, removed: functionRemoved, added: [], changed: [] });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Instruction breakpoints
|
|
476
|
+
const instructionRemoved: DebugInstructionBreakpoint[] = [];
|
|
477
|
+
this.instructionBreakpoints = this.instructionBreakpoints.filter(bp => {
|
|
478
|
+
if (toRemove.has(bp.id)) {
|
|
479
|
+
instructionRemoved.push(bp);
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
return true;
|
|
483
|
+
});
|
|
484
|
+
if (instructionRemoved.length) {
|
|
485
|
+
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
486
|
+
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri: BreakpointManager.INSTRUCTION_URI, removed: instructionRemoved, added: [], changed: [] });
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Data breakpoints
|
|
490
|
+
const dataRemoved: DebugDataBreakpoint[] = [];
|
|
491
|
+
this.dataBreakpoints = this.dataBreakpoints.filter(bp => {
|
|
492
|
+
if (toRemove.has(bp.id)) {
|
|
493
|
+
dataRemoved.push(bp);
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
return true;
|
|
497
|
+
});
|
|
498
|
+
if (dataRemoved.length) {
|
|
499
|
+
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
500
|
+
this.onDidChangeDataBreakpointsEmitter.fire({ uri: BreakpointManager.DATA_URI, removed: dataRemoved, added: [], changed: [] });
|
|
202
501
|
}
|
|
203
502
|
}
|
|
204
503
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
504
|
+
// ── Exception breakpoints ──
|
|
505
|
+
|
|
506
|
+
protected exceptionBreakpoints = new Array<DebugExceptionBreakpoint>();
|
|
507
|
+
|
|
508
|
+
getExceptionBreakpoint(filter: DebugProtocol.ExceptionBreakpointsFilter): DebugExceptionBreakpoint | undefined {
|
|
509
|
+
return this.exceptionBreakpoints.find(candidate => ExceptionBreakpoint.matches(candidate.origin.raw, filter));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
getExceptionBreakpoints(): readonly DebugExceptionBreakpoint[] {
|
|
513
|
+
return this.exceptionBreakpoints;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
addExceptionBreakpoints(filters: DebugProtocol.ExceptionBreakpointsFilter[], sessionId: string): void {
|
|
517
|
+
for (const filter of filters) {
|
|
518
|
+
let bp = this.exceptionBreakpoints.find(candidate => ExceptionBreakpoint.matches(candidate.origin.raw, filter));
|
|
519
|
+
if (!bp) {
|
|
520
|
+
bp = DebugExceptionBreakpoint.create(ExceptionBreakpoint.create(filter), this.getBreakpointOptions());
|
|
521
|
+
this.exceptionBreakpoints.push(bp);
|
|
522
|
+
}
|
|
523
|
+
bp.setSessionEnablement(sessionId, true);
|
|
524
|
+
this.doUpdateExceptionBreakpointVisibility(sessionId);
|
|
210
525
|
}
|
|
526
|
+
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
|
|
211
527
|
}
|
|
212
528
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
529
|
+
updateExceptionBreakpointVisibility(sessionId: string): void {
|
|
530
|
+
this.doUpdateExceptionBreakpointVisibility(sessionId);
|
|
531
|
+
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
clearExceptionSessionEnablement(sessionId: string): void {
|
|
535
|
+
for (const bp of this.exceptionBreakpoints) {
|
|
536
|
+
bp.setSessionEnablement(sessionId, false);
|
|
537
|
+
}
|
|
538
|
+
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
protected doUpdateExceptionBreakpointVisibility(sessionId: string): void {
|
|
542
|
+
for (const bp of this.exceptionBreakpoints) {
|
|
543
|
+
bp.setPersistentVisibility(bp.isEnabledForSession(sessionId));
|
|
218
544
|
}
|
|
219
545
|
}
|
|
220
546
|
|
|
221
|
-
|
|
547
|
+
// ── Function breakpoints ──
|
|
222
548
|
|
|
223
|
-
|
|
549
|
+
protected functionBreakpoints: DebugFunctionBreakpoint[] = [];
|
|
550
|
+
|
|
551
|
+
getFunctionBreakpoints(): readonly DebugFunctionBreakpoint[] {
|
|
224
552
|
return this.functionBreakpoints;
|
|
225
553
|
}
|
|
226
554
|
|
|
227
555
|
setFunctionBreakpoints(functionBreakpoints: FunctionBreakpoint[]): void {
|
|
228
|
-
const oldBreakpoints = new Map(this.functionBreakpoints.map(b => [b.id, b]
|
|
556
|
+
const oldBreakpoints = new Map(this.functionBreakpoints.map(b => [b.id, b]));
|
|
229
557
|
|
|
230
|
-
this.functionBreakpoints = functionBreakpoints
|
|
558
|
+
this.functionBreakpoints = functionBreakpoints.map(bp => {
|
|
559
|
+
const existing = oldBreakpoints.get(bp.id);
|
|
560
|
+
if (existing) { return existing; }
|
|
561
|
+
return DebugFunctionBreakpoint.create(bp, this.getBreakpointOptions());
|
|
562
|
+
});
|
|
231
563
|
this.fireOnDidChangeMarkers(BreakpointManager.FUNCTION_URI);
|
|
232
564
|
|
|
233
|
-
const added:
|
|
234
|
-
const removed:
|
|
235
|
-
const changed:
|
|
565
|
+
const added: DebugFunctionBreakpoint[] = [];
|
|
566
|
+
const removed: DebugFunctionBreakpoint[] = [];
|
|
567
|
+
const changed: DebugFunctionBreakpoint[] = [];
|
|
236
568
|
const ids = new Set<string>();
|
|
237
|
-
for (const newBreakpoint of functionBreakpoints) {
|
|
569
|
+
for (const newBreakpoint of this.functionBreakpoints) {
|
|
238
570
|
ids.add(newBreakpoint.id);
|
|
239
571
|
if (oldBreakpoints.has(newBreakpoint.id)) {
|
|
240
572
|
changed.push(newBreakpoint);
|
|
@@ -250,50 +582,102 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
250
582
|
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri: BreakpointManager.FUNCTION_URI, added, removed, changed });
|
|
251
583
|
}
|
|
252
584
|
|
|
253
|
-
|
|
585
|
+
addFunctionBreakpoint(bp: FunctionBreakpoint): void {
|
|
586
|
+
const duplicate = this.functionBreakpoints.find(c => c.origin.raw.name === bp.raw.name);
|
|
587
|
+
if (duplicate) { return; }
|
|
588
|
+
const newBp = DebugFunctionBreakpoint.create(bp, this.getBreakpointOptions());
|
|
589
|
+
this.functionBreakpoints = [...this.functionBreakpoints, newBp];
|
|
590
|
+
this.fireOnDidChangeMarkers(newBp.uri);
|
|
591
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri: newBp.uri, added: [newBp], changed: [], removed: [] });
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
updateFunctionBreakpoint(bp: DebugFunctionBreakpoint, update: Partial<DebugProtocol.FunctionBreakpoint>): void {
|
|
595
|
+
if (!this.functionBreakpoints.includes(bp)) { return; }
|
|
596
|
+
const removed: DebugFunctionBreakpoint[] = [];
|
|
597
|
+
if ('name' in update && !update.name) {
|
|
598
|
+
throw new Error('Name field of function breakpoint must be populated.');
|
|
599
|
+
} else if ('name' in update) {
|
|
600
|
+
this.functionBreakpoints = this.functionBreakpoints.filter(candidate => {
|
|
601
|
+
if (candidate !== bp && candidate.origin.raw.name === update.name) {
|
|
602
|
+
removed.push(candidate);
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
return true;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
bp.origin.raw = { ...bp.origin.raw, ...update };
|
|
609
|
+
this.fireOnDidChangeMarkers(bp.uri);
|
|
610
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri: bp.uri, changed: [bp], removed, added: [] });
|
|
611
|
+
}
|
|
254
612
|
|
|
255
|
-
|
|
256
|
-
|
|
613
|
+
removeFunctionBreakpoint(breakpoint: DebugFunctionBreakpoint): void {
|
|
614
|
+
const index = this.functionBreakpoints.indexOf(breakpoint);
|
|
615
|
+
if (index === -1) { return; }
|
|
616
|
+
const removed = this.functionBreakpoints.splice(index, 1);
|
|
617
|
+
this.fireOnDidChangeMarkers(breakpoint.uri);
|
|
618
|
+
this.onDidChangeFunctionBreakpointsEmitter.fire({ uri: breakpoint.uri, removed, added: [], changed: [] });
|
|
257
619
|
}
|
|
258
620
|
|
|
259
|
-
|
|
260
|
-
|
|
621
|
+
// ── Instruction breakpoints ──
|
|
622
|
+
|
|
623
|
+
protected instructionBreakpoints: DebugInstructionBreakpoint[] = [];
|
|
624
|
+
|
|
625
|
+
getInstructionBreakpoints(): ReadonlyArray<DebugInstructionBreakpoint> {
|
|
626
|
+
return this.instructionBreakpoints;
|
|
261
627
|
}
|
|
262
628
|
|
|
263
629
|
protected setInstructionBreakpoints(newBreakpoints: InstructionBreakpoint[]): void {
|
|
264
|
-
const
|
|
265
|
-
|
|
630
|
+
const oldBreakpoints = new Map(this.instructionBreakpoints.map(bp => [bp.id, bp]));
|
|
631
|
+
const currentBreakpoints = newBreakpoints.map(bp => {
|
|
632
|
+
const existing = oldBreakpoints.get(bp.id);
|
|
633
|
+
if (existing) { return existing; }
|
|
634
|
+
return DebugInstructionBreakpoint.create(bp, this.getBreakpointOptions());
|
|
635
|
+
});
|
|
636
|
+
const added: DebugInstructionBreakpoint[] = [];
|
|
637
|
+
const changed: DebugInstructionBreakpoint[] = [];
|
|
638
|
+
for (const breakpoint of currentBreakpoints) {
|
|
639
|
+
const old = oldBreakpoints.get(breakpoint.id);
|
|
640
|
+
if (old) {
|
|
641
|
+
changed.push(old);
|
|
642
|
+
} else {
|
|
643
|
+
added.push(breakpoint);
|
|
644
|
+
}
|
|
645
|
+
oldBreakpoints.delete(breakpoint.id);
|
|
646
|
+
}
|
|
647
|
+
const removed = Array.from(oldBreakpoints.values());
|
|
648
|
+
this.instructionBreakpoints = currentBreakpoints;
|
|
266
649
|
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
267
650
|
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri: BreakpointManager.INSTRUCTION_URI, added, removed, changed });
|
|
268
651
|
}
|
|
269
652
|
|
|
270
653
|
addInstructionBreakpoint(address: string, offset: number, condition?: string, hitCondition?: string): void {
|
|
271
|
-
this.
|
|
654
|
+
const duplicate = this.instructionBreakpoints.find(
|
|
655
|
+
c => c.origin.raw.instructionReference === address && (c.origin.raw.offset ?? 0) === (offset ?? 0)
|
|
656
|
+
);
|
|
657
|
+
if (duplicate) { return; }
|
|
658
|
+
const newBp = DebugInstructionBreakpoint.create(InstructionBreakpoint.create({
|
|
272
659
|
instructionReference: address,
|
|
273
660
|
offset,
|
|
274
661
|
condition,
|
|
275
662
|
hitCondition,
|
|
276
|
-
})));
|
|
663
|
+
}), this.getBreakpointOptions());
|
|
664
|
+
this.instructionBreakpoints = [...this.instructionBreakpoints, newBp];
|
|
665
|
+
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
666
|
+
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri: BreakpointManager.INSTRUCTION_URI, added: [newBp], removed: [], changed: [] });
|
|
277
667
|
}
|
|
278
668
|
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
669
|
+
removeInstructionBreakpoint(breakpoint: DebugInstructionBreakpoint): void {
|
|
670
|
+
const index = this.instructionBreakpoints.indexOf(breakpoint);
|
|
671
|
+
if (index === -1) { return; }
|
|
672
|
+
const removed = this.instructionBreakpoints.splice(index, 1);
|
|
673
|
+
this.fireOnDidChangeMarkers(breakpoint.uri);
|
|
674
|
+
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri: breakpoint.uri, removed, added: [], changed: [] });
|
|
286
675
|
}
|
|
287
676
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const breakpointIndex = this.instructionBreakpoints.findIndex(breakpoint => breakpoint.instructionReference === address);
|
|
293
|
-
if (breakpointIndex !== -1) {
|
|
294
|
-
const removed = this.instructionBreakpoints.splice(breakpointIndex, 1);
|
|
295
|
-
this.fireOnDidChangeMarkers(BreakpointManager.INSTRUCTION_URI);
|
|
296
|
-
this.onDidChangeInstructionBreakpointsEmitter.fire({ uri: BreakpointManager.INSTRUCTION_URI, added: [], changed: [], removed });
|
|
677
|
+
removeInstructionBreakpointAt(address: string): void {
|
|
678
|
+
const match = this.instructionBreakpoints.find(candidate => candidate.origin.raw.instructionReference === address);
|
|
679
|
+
if (match) {
|
|
680
|
+
this.removeInstructionBreakpoint(match);
|
|
297
681
|
}
|
|
298
682
|
}
|
|
299
683
|
|
|
@@ -301,46 +685,66 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
301
685
|
this.setInstructionBreakpoints([]);
|
|
302
686
|
}
|
|
303
687
|
|
|
304
|
-
|
|
688
|
+
// ── Data breakpoints ──
|
|
305
689
|
|
|
306
|
-
|
|
307
|
-
|
|
690
|
+
protected dataBreakpoints: DebugDataBreakpoint[] = [];
|
|
691
|
+
|
|
692
|
+
getDataBreakpoints(): readonly DebugDataBreakpoint[] {
|
|
693
|
+
return this.dataBreakpoints;
|
|
308
694
|
}
|
|
309
695
|
|
|
310
696
|
setDataBreakpoints(breakpoints: DataBreakpoint[]): void {
|
|
311
|
-
const
|
|
312
|
-
|
|
697
|
+
const oldBreakpoints = new Map(this.dataBreakpoints.map(bp => [bp.id, bp]));
|
|
698
|
+
const newBreakpoints = breakpoints.map(bp => {
|
|
699
|
+
const existing = oldBreakpoints.get(bp.id);
|
|
700
|
+
if (existing) { return existing; }
|
|
701
|
+
return DebugDataBreakpoint.create(bp, this.getBreakpointOptions());
|
|
702
|
+
});
|
|
703
|
+
const added: DebugDataBreakpoint[] = [];
|
|
704
|
+
const changed: DebugDataBreakpoint[] = [];
|
|
705
|
+
for (const bp of newBreakpoints) {
|
|
706
|
+
if (oldBreakpoints.has(bp.id)) {
|
|
707
|
+
changed.push(bp);
|
|
708
|
+
} else {
|
|
709
|
+
added.push(bp);
|
|
710
|
+
}
|
|
711
|
+
oldBreakpoints.delete(bp.id);
|
|
712
|
+
}
|
|
713
|
+
const removed = Array.from(oldBreakpoints.values());
|
|
714
|
+
this.dataBreakpoints = newBreakpoints;
|
|
313
715
|
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
314
716
|
this.onDidChangeDataBreakpointsEmitter.fire({ uri: BreakpointManager.DATA_URI, added, removed, changed });
|
|
315
717
|
}
|
|
316
718
|
|
|
317
719
|
addDataBreakpoint(breakpoint: DataBreakpoint): void {
|
|
318
|
-
this.
|
|
720
|
+
const duplicate = this.dataBreakpoints.find(c => c.origin.raw.dataId === breakpoint.raw.dataId);
|
|
721
|
+
if (duplicate) { return; }
|
|
722
|
+
const newBp = DebugDataBreakpoint.create(breakpoint, this.getBreakpointOptions());
|
|
723
|
+
this.dataBreakpoints = [...this.dataBreakpoints, newBp];
|
|
724
|
+
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
725
|
+
this.onDidChangeDataBreakpointsEmitter.fire({ uri: BreakpointManager.DATA_URI, added: [newBp], removed: [], changed: [] });
|
|
319
726
|
}
|
|
320
727
|
|
|
321
|
-
updateDataBreakpoint(
|
|
322
|
-
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
728
|
+
updateDataBreakpoint(bp: DebugDataBreakpoint, options: { enabled?: boolean; raw?: Partial<Omit<DebugProtocol.DataBreakpoint, 'dataId'>> }): void {
|
|
729
|
+
if (!this.dataBreakpoints.includes(bp)) { return; }
|
|
730
|
+
if (options.raw) {
|
|
731
|
+
Object.assign(bp.origin.raw, options.raw);
|
|
732
|
+
}
|
|
733
|
+
if (options.enabled !== undefined) {
|
|
734
|
+
bp.origin.enabled = options.enabled;
|
|
735
|
+
}
|
|
736
|
+
this.fireBreakpointChanged(bp);
|
|
328
737
|
}
|
|
329
738
|
|
|
330
|
-
removeDataBreakpoint(
|
|
331
|
-
const index = this.dataBreakpoints.
|
|
739
|
+
removeDataBreakpoint(bp: DebugDataBreakpoint): void {
|
|
740
|
+
const index = this.dataBreakpoints.indexOf(bp);
|
|
332
741
|
if (index < 0) { return; }
|
|
333
|
-
const removed = this.dataBreakpoints.splice(index);
|
|
742
|
+
const removed = this.dataBreakpoints.splice(index, 1);
|
|
334
743
|
this.fireOnDidChangeMarkers(BreakpointManager.DATA_URI);
|
|
335
744
|
this.onDidChangeDataBreakpointsEmitter.fire({ uri: BreakpointManager.DATA_URI, added: [], removed, changed: [] });
|
|
336
745
|
}
|
|
337
746
|
|
|
338
|
-
|
|
339
|
-
this.cleanAllMarkers();
|
|
340
|
-
this.setFunctionBreakpoints([]);
|
|
341
|
-
this.setInstructionBreakpoints([]);
|
|
342
|
-
this.setDataBreakpoints([]);
|
|
343
|
-
}
|
|
747
|
+
// ── Persistence ──
|
|
344
748
|
|
|
345
749
|
async load(): Promise<void> {
|
|
346
750
|
const data = await this.storage.getData<BreakpointManager.Data>('breakpoints', {
|
|
@@ -356,11 +760,15 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
356
760
|
this.setFunctionBreakpoints(data.functionBreakpoints);
|
|
357
761
|
}
|
|
358
762
|
if (data.exceptionBreakpoints) {
|
|
359
|
-
this.
|
|
763
|
+
this.exceptionBreakpoints = data.exceptionBreakpoints.map(bp => DebugExceptionBreakpoint.create(bp, this.getBreakpointOptions()));
|
|
764
|
+
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
|
|
360
765
|
}
|
|
361
766
|
if (data.instructionBreakpoints) {
|
|
362
767
|
this.setInstructionBreakpoints(data.instructionBreakpoints);
|
|
363
768
|
}
|
|
769
|
+
if (data.dataBreakpoints) {
|
|
770
|
+
this.setDataBreakpoints(data.dataBreakpoints);
|
|
771
|
+
}
|
|
364
772
|
}
|
|
365
773
|
|
|
366
774
|
save(): void {
|
|
@@ -368,28 +776,27 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
|
|
|
368
776
|
breakpointsEnabled: this._breakpointsEnabled,
|
|
369
777
|
breakpoints: {}
|
|
370
778
|
};
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
data.breakpoints[uri] = this.findMarkers({ uri: new URI(uri) }).map(marker => marker.data);
|
|
779
|
+
for (const uri of this.getUris()) {
|
|
780
|
+
data.breakpoints[uri] = (this.sourceBreakpoints.get(uri) ?? []).map(bp => bp.origin);
|
|
374
781
|
}
|
|
375
782
|
if (this.functionBreakpoints.length) {
|
|
376
|
-
data.functionBreakpoints = this.functionBreakpoints;
|
|
783
|
+
data.functionBreakpoints = this.functionBreakpoints.map(({ origin }) => origin);
|
|
377
784
|
}
|
|
378
|
-
if (this.exceptionBreakpoints.
|
|
379
|
-
data.exceptionBreakpoints =
|
|
785
|
+
if (this.exceptionBreakpoints.length) {
|
|
786
|
+
data.exceptionBreakpoints = this.exceptionBreakpoints.filter(candidate => candidate.isPersistentlyVisible()).map(({ origin }) => origin);
|
|
380
787
|
}
|
|
381
788
|
if (this.instructionBreakpoints.length) {
|
|
382
|
-
data.instructionBreakpoints = this.instructionBreakpoints;
|
|
789
|
+
data.instructionBreakpoints = this.instructionBreakpoints.map(({ origin }) => origin);
|
|
383
790
|
}
|
|
384
|
-
const dataBreakpointsToStore = this.dataBreakpoints.filter(candidate => candidate.info.canPersist);
|
|
791
|
+
const dataBreakpointsToStore = this.dataBreakpoints.filter(candidate => candidate.origin.info.canPersist);
|
|
385
792
|
if (dataBreakpointsToStore.length) {
|
|
386
|
-
data.dataBreakpoints = dataBreakpointsToStore;
|
|
793
|
+
data.dataBreakpoints = dataBreakpointsToStore.map(({ origin }) => origin);
|
|
387
794
|
}
|
|
388
795
|
|
|
389
796
|
this.storage.setData('breakpoints', data);
|
|
390
797
|
}
|
|
391
|
-
|
|
392
798
|
}
|
|
799
|
+
|
|
393
800
|
export namespace BreakpointManager {
|
|
394
801
|
export interface Data {
|
|
395
802
|
breakpointsEnabled: boolean;
|
|
@@ -402,21 +809,3 @@ export namespace BreakpointManager {
|
|
|
402
809
|
dataBreakpoints?: DataBreakpoint[];
|
|
403
810
|
}
|
|
404
811
|
}
|
|
405
|
-
|
|
406
|
-
export function diff<T>(prevs: T[], nexts: T[], toKey: (member: T) => string): { added: T[], removed: T[], changed: T[] } {
|
|
407
|
-
const old = new Map(prevs.map(item => [toKey(item), item]));
|
|
408
|
-
const current = new Map(nexts.map(item => [toKey(item), item]));
|
|
409
|
-
const added = [];
|
|
410
|
-
const changed = [];
|
|
411
|
-
for (const [id, next] of current.entries()) {
|
|
412
|
-
const prev = old.get(id);
|
|
413
|
-
if (prev) {
|
|
414
|
-
changed.push(prev);
|
|
415
|
-
} else {
|
|
416
|
-
added.push(next);
|
|
417
|
-
}
|
|
418
|
-
old.delete(id);
|
|
419
|
-
}
|
|
420
|
-
const removed = Array.from(old.values());
|
|
421
|
-
return { added, removed, changed };
|
|
422
|
-
}
|