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