@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
|
@@ -58,13 +58,24 @@ export class DebugEditorModel implements Disposable {
|
|
|
58
58
|
protected uri: URI;
|
|
59
59
|
|
|
60
60
|
protected breakpointDecorations: string[] = [];
|
|
61
|
-
protected breakpointRanges = new Map<string, [monaco.Range,
|
|
61
|
+
protected breakpointRanges = new Map<string, [monaco.Range, DebugSourceBreakpoint]>();
|
|
62
62
|
|
|
63
63
|
protected currentBreakpointDecorations: string[] = [];
|
|
64
64
|
|
|
65
65
|
protected editorDecorations: string[] = [];
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Set during `render()` to prevent `onDidChangeDecorations` → `updateBreakpoints()`
|
|
69
|
+
* from reading back the decoration positions we just wrote, which would produce
|
|
70
|
+
* a spurious or circular `setBreakpoints` call.
|
|
71
|
+
*/
|
|
72
|
+
protected ignoreDecorationsChangedEvent = false;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set during `updateBreakpoints()` to prevent the resulting
|
|
76
|
+
* `onDidChangeBreakpoints` from re-entering `render()`.
|
|
77
|
+
*/
|
|
78
|
+
protected ignoreBreakpointsChangeEvent = false;
|
|
68
79
|
protected toDisposeOnModelChange = new DisposableCollection();
|
|
69
80
|
|
|
70
81
|
@inject(DebugHoverWidget)
|
|
@@ -111,12 +122,12 @@ export class DebugEditorModel implements Disposable {
|
|
|
111
122
|
this.sessions.onDidChange(() => this.update()),
|
|
112
123
|
this.toDisposeOnUpdate,
|
|
113
124
|
Disposable.create(() => this.toDisposeOnModelChange.dispose()),
|
|
114
|
-
this.
|
|
115
|
-
if (
|
|
125
|
+
this.breakpoints.onDidChangeBreakpoints(event => {
|
|
126
|
+
if (!this.ignoreBreakpointsChangeEvent) {
|
|
116
127
|
this.render();
|
|
117
128
|
}
|
|
129
|
+
this.closeBreakpointIfAffected(event);
|
|
118
130
|
}),
|
|
119
|
-
this.breakpoints.onDidChangeBreakpoints(event => this.closeBreakpointIfAffected(event)),
|
|
120
131
|
]);
|
|
121
132
|
this.updateModel();
|
|
122
133
|
}
|
|
@@ -237,8 +248,13 @@ export class DebugEditorModel implements Disposable {
|
|
|
237
248
|
}
|
|
238
249
|
|
|
239
250
|
render(): void {
|
|
240
|
-
this.
|
|
241
|
-
|
|
251
|
+
this.ignoreDecorationsChangedEvent = true;
|
|
252
|
+
try {
|
|
253
|
+
this.renderBreakpoints();
|
|
254
|
+
this.renderCurrentBreakpoints();
|
|
255
|
+
} finally {
|
|
256
|
+
this.ignoreDecorationsChangedEvent = false;
|
|
257
|
+
}
|
|
242
258
|
}
|
|
243
259
|
protected renderBreakpoints(): void {
|
|
244
260
|
const breakpoints = this.breakpoints.getBreakpoints(this.uri);
|
|
@@ -246,12 +262,12 @@ export class DebugEditorModel implements Disposable {
|
|
|
246
262
|
this.breakpointDecorations = this.deltaDecorations(this.breakpointDecorations, decorations);
|
|
247
263
|
this.updateBreakpointRanges(breakpoints);
|
|
248
264
|
}
|
|
249
|
-
protected createBreakpointDecorations(breakpoints:
|
|
265
|
+
protected createBreakpointDecorations(breakpoints: readonly DebugSourceBreakpoint[]): monaco.editor.IModelDeltaDecoration[] {
|
|
250
266
|
return breakpoints.map(breakpoint => this.createBreakpointDecoration(breakpoint));
|
|
251
267
|
}
|
|
252
|
-
protected createBreakpointDecoration(breakpoint:
|
|
253
|
-
const lineNumber = breakpoint.
|
|
254
|
-
const column = breakpoint.
|
|
268
|
+
protected createBreakpointDecoration(breakpoint: DebugSourceBreakpoint): monaco.editor.IModelDeltaDecoration {
|
|
269
|
+
const lineNumber = breakpoint.line;
|
|
270
|
+
const column = breakpoint.column || this.editor.getControl().getModel()?.getLineFirstNonWhitespaceColumn(lineNumber) || 1;
|
|
255
271
|
const range = new monaco.Range(lineNumber, column, lineNumber, column + 1);
|
|
256
272
|
return {
|
|
257
273
|
range,
|
|
@@ -261,7 +277,7 @@ export class DebugEditorModel implements Disposable {
|
|
|
261
277
|
};
|
|
262
278
|
}
|
|
263
279
|
|
|
264
|
-
protected updateBreakpointRanges(breakpoints:
|
|
280
|
+
protected updateBreakpointRanges(breakpoints: readonly DebugSourceBreakpoint[]): void {
|
|
265
281
|
this.breakpointRanges.clear();
|
|
266
282
|
for (let i = 0; i < this.breakpointDecorations.length; i++) {
|
|
267
283
|
const decoration = this.breakpointDecorations[i];
|
|
@@ -278,8 +294,19 @@ export class DebugEditorModel implements Disposable {
|
|
|
278
294
|
this.currentBreakpointDecorations = this.deltaDecorations(this.currentBreakpointDecorations, decorations);
|
|
279
295
|
}
|
|
280
296
|
protected createCurrentBreakpointDecorations(): monaco.editor.IModelDeltaDecoration[] {
|
|
281
|
-
const breakpoints = this.
|
|
282
|
-
|
|
297
|
+
const breakpoints = this.breakpoints.getBreakpoints(this.uri);
|
|
298
|
+
// Deduplicate by rendered position: when multiple breakpoints resolve
|
|
299
|
+
// to the same (line, column) — e.g. via source-map collapsing — only
|
|
300
|
+
// create one decoration to avoid double dots in the editor.
|
|
301
|
+
const seen = new Set<string>();
|
|
302
|
+
const result: monaco.editor.IModelDeltaDecoration[] = [];
|
|
303
|
+
for (const bp of breakpoints) {
|
|
304
|
+
const key = `${bp.line}:${bp.column ?? 0}`;
|
|
305
|
+
if (seen.has(key)) { continue; }
|
|
306
|
+
seen.add(key);
|
|
307
|
+
result.push(this.createCurrentBreakpointDecoration(bp));
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
283
310
|
}
|
|
284
311
|
protected createCurrentBreakpointDecoration(breakpoint: DebugSourceBreakpoint): monaco.editor.IModelDeltaDecoration {
|
|
285
312
|
const lineNumber = breakpoint.line;
|
|
@@ -301,11 +328,16 @@ export class DebugEditorModel implements Disposable {
|
|
|
301
328
|
protected updateBreakpoints(): void {
|
|
302
329
|
if (this.areBreakpointsAffected()) {
|
|
303
330
|
const breakpoints = this.createBreakpoints();
|
|
304
|
-
this.
|
|
331
|
+
this.ignoreBreakpointsChangeEvent = true;
|
|
332
|
+
try {
|
|
333
|
+
this.breakpoints.setBreakpoints(this.uri, breakpoints);
|
|
334
|
+
} finally {
|
|
335
|
+
this.ignoreBreakpointsChangeEvent = false;
|
|
336
|
+
}
|
|
305
337
|
}
|
|
306
338
|
}
|
|
307
339
|
protected areBreakpointsAffected(): boolean {
|
|
308
|
-
if (this.
|
|
340
|
+
if (this.ignoreDecorationsChangedEvent || !this.editor.getControl().getModel()) {
|
|
309
341
|
return false;
|
|
310
342
|
}
|
|
311
343
|
for (const decoration of this.breakpointDecorations) {
|
|
@@ -328,11 +360,11 @@ export class DebugEditorModel implements Disposable {
|
|
|
328
360
|
const column = range.startColumn;
|
|
329
361
|
const oldBreakpoint = this.breakpointRanges.get(decoration)?.[1];
|
|
330
362
|
if (oldBreakpoint) {
|
|
331
|
-
const isLineBreakpoint = oldBreakpoint.raw.line !== undefined && oldBreakpoint.raw.column === undefined;
|
|
363
|
+
const isLineBreakpoint = oldBreakpoint.origin.raw.line !== undefined && oldBreakpoint.origin.raw.column === undefined;
|
|
332
364
|
const position = isLineBreakpoint ? `${line}` : `${line}:${column}`;
|
|
333
365
|
if (!positions.has(position)) {
|
|
334
366
|
const change = isLineBreakpoint ? { line } : { line, column };
|
|
335
|
-
const breakpoint = SourceBreakpoint.create(uri, change, oldBreakpoint);
|
|
367
|
+
const breakpoint = SourceBreakpoint.create(uri, change, oldBreakpoint.origin);
|
|
336
368
|
breakpoints.push(breakpoint);
|
|
337
369
|
positions.add(position);
|
|
338
370
|
}
|
|
@@ -345,16 +377,17 @@ export class DebugEditorModel implements Disposable {
|
|
|
345
377
|
get position(): monaco.Position {
|
|
346
378
|
return this.editor.getControl().getPosition()!;
|
|
347
379
|
}
|
|
380
|
+
|
|
348
381
|
getBreakpoint(position: monaco.Position = this.position): DebugSourceBreakpoint | undefined {
|
|
349
382
|
return this.getInlineBreakpoint(position) || this.getLineBreakpoints(position)[0];
|
|
350
383
|
}
|
|
351
384
|
|
|
352
385
|
getInlineBreakpoint(position: monaco.Position = this.position): DebugSourceBreakpoint | undefined {
|
|
353
|
-
return this.
|
|
386
|
+
return this.breakpoints.getBreakpoints(this.uri).find(candidate => candidate.line === position.lineNumber && candidate.column === position.column);
|
|
354
387
|
}
|
|
355
388
|
|
|
356
389
|
protected getLineBreakpoints(position: monaco.Position = this.position): DebugSourceBreakpoint[] {
|
|
357
|
-
return this.
|
|
390
|
+
return this.breakpoints.getBreakpoints(this.uri).filter(candidate => candidate.line === position.lineNumber);
|
|
358
391
|
}
|
|
359
392
|
|
|
360
393
|
protected addBreakpoint(raw: DebugProtocol.SourceBreakpoint): void {
|
|
@@ -374,13 +407,8 @@ export class DebugEditorModel implements Disposable {
|
|
|
374
407
|
}
|
|
375
408
|
|
|
376
409
|
addInlineBreakpoint(): void {
|
|
377
|
-
const { position } = this;
|
|
378
|
-
|
|
379
|
-
const breakpoint = this.getInlineBreakpoint(position);
|
|
380
|
-
if (breakpoint) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
this.addBreakpoint({ line: lineNumber, column });
|
|
410
|
+
const { position: { lineNumber: line, column } } = this;
|
|
411
|
+
this.addBreakpoint({ line, column });
|
|
384
412
|
}
|
|
385
413
|
|
|
386
414
|
acceptBreakpoint(): void {
|
|
@@ -388,7 +416,7 @@ export class DebugEditorModel implements Disposable {
|
|
|
388
416
|
if (position && values) {
|
|
389
417
|
const breakpoint = position.column > 0 ? this.getInlineBreakpoint(position) : this.getLineBreakpoints(position)[0];
|
|
390
418
|
if (breakpoint) {
|
|
391
|
-
|
|
419
|
+
this.breakpoints.updateBreakpoint(breakpoint, values);
|
|
392
420
|
} else {
|
|
393
421
|
const { lineNumber } = position;
|
|
394
422
|
const column = position.column > 0 ? position.column : undefined;
|
|
@@ -446,7 +474,7 @@ export class DebugEditorModel implements Disposable {
|
|
|
446
474
|
return;
|
|
447
475
|
}
|
|
448
476
|
for (const breakpoint of removed) {
|
|
449
|
-
if (breakpoint.
|
|
477
|
+
if (breakpoint.line === position.lineNumber) {
|
|
450
478
|
this.breakpointWidget.hide();
|
|
451
479
|
break;
|
|
452
480
|
}
|
|
@@ -479,12 +507,7 @@ export class DebugEditorModel implements Disposable {
|
|
|
479
507
|
}
|
|
480
508
|
|
|
481
509
|
protected deltaDecorations(oldDecorations: string[], newDecorations: monaco.editor.IModelDeltaDecoration[]): string[] {
|
|
482
|
-
this.
|
|
483
|
-
try {
|
|
484
|
-
return this.editor.getControl().deltaDecorations(oldDecorations, newDecorations);
|
|
485
|
-
} finally {
|
|
486
|
-
this.updatingDecorations = false;
|
|
487
|
-
}
|
|
510
|
+
return this.editor.getControl().deltaDecorations(oldDecorations, newDecorations);
|
|
488
511
|
}
|
|
489
512
|
|
|
490
513
|
static STICKINESS = monaco.editor.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { MaybePromise, URI } from '@theia/core';
|
|
19
|
+
import { OpenHandler, OpenerOptions } from '@theia/core/lib/browser';
|
|
20
|
+
import { EditorManager } from '@theia/editor/lib/browser';
|
|
21
|
+
import { BreakpointManager } from '../breakpoint/breakpoint-manager';
|
|
22
|
+
import { DebugSessionManager } from '../debug-session-manager';
|
|
23
|
+
import { DEBUG_BREAKPOINT_SCHEME } from '../breakpoint/breakpoint-marker';
|
|
24
|
+
|
|
25
|
+
@injectable()
|
|
26
|
+
export class DebugBreakpointOpener implements OpenHandler {
|
|
27
|
+
@inject(BreakpointManager) protected readonly breakpointManager: BreakpointManager;
|
|
28
|
+
@inject(DebugSessionManager) protected readonly sessionManager: DebugSessionManager;
|
|
29
|
+
@inject(EditorManager) protected readonly editorManager: EditorManager;
|
|
30
|
+
|
|
31
|
+
readonly id = 'debug-breakpoint-opener';
|
|
32
|
+
|
|
33
|
+
canHandle(uri: URI, options?: OpenerOptions | undefined): MaybePromise<number> {
|
|
34
|
+
return uri.scheme === DEBUG_BREAKPOINT_SCHEME ? 150 : -1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
open(uri: URI, options?: OpenerOptions | undefined): MaybePromise<object | undefined> {
|
|
38
|
+
if (uri.scheme !== DEBUG_BREAKPOINT_SCHEME) { throw new Error(`Unexpected scheme. Expected '${DEBUG_BREAKPOINT_SCHEME}' but got '${uri.scheme}'.`); }
|
|
39
|
+
const bpId = uri.authority;
|
|
40
|
+
const bp = this.breakpointManager.getBreakpointById(bpId);
|
|
41
|
+
if (!bp) { return; }
|
|
42
|
+
if (bp.raw?.source) {
|
|
43
|
+
const session = this.sessionManager.getSession(bp.raw.sessionId);
|
|
44
|
+
const source = session?.getSource(bp.raw.source);
|
|
45
|
+
if (source) {
|
|
46
|
+
return source.open(options);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return this.editorManager.open(bp.uri, options);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -17,24 +17,19 @@
|
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
18
|
import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol';
|
|
19
19
|
import URI from '@theia/core/lib/common/uri';
|
|
20
|
-
import {
|
|
21
|
-
import { LabelProvider, DISABLED_CLASS, TreeWidget } from '@theia/core/lib/browser';
|
|
20
|
+
import { CommandService } from '@theia/core/lib/common';
|
|
21
|
+
import { LabelProvider, DISABLED_CLASS, OpenerService, TreeWidget } from '@theia/core/lib/browser';
|
|
22
22
|
import { TreeElement } from '@theia/core/lib/browser/source-tree';
|
|
23
23
|
import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
|
|
24
|
-
import { DebugSession } from '../debug-session';
|
|
25
24
|
import { BaseBreakpoint } from '../breakpoint/breakpoint-marker';
|
|
26
25
|
import { BreakpointManager } from '../breakpoint/breakpoint-manager';
|
|
27
26
|
import { nls } from '@theia/core';
|
|
28
27
|
|
|
29
|
-
export
|
|
30
|
-
readonly raw?: DebugProtocol.Breakpoint;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class DebugBreakpointOptions {
|
|
28
|
+
export interface DebugBreakpointOptions {
|
|
34
29
|
readonly labelProvider: LabelProvider;
|
|
35
30
|
readonly breakpoints: BreakpointManager;
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
31
|
+
readonly openerService: OpenerService;
|
|
32
|
+
readonly commandService: CommandService;
|
|
38
33
|
}
|
|
39
34
|
|
|
40
35
|
export class DebugBreakpointDecoration {
|
|
@@ -42,27 +37,102 @@ export class DebugBreakpointDecoration {
|
|
|
42
37
|
readonly message: string[];
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
export
|
|
40
|
+
export type BPCapabilities = Required<Pick<
|
|
41
|
+
DebugProtocol.Capabilities,
|
|
42
|
+
| 'supportsConditionalBreakpoints'
|
|
43
|
+
| 'supportsHitConditionalBreakpoints'
|
|
44
|
+
| 'supportsLogPoints'
|
|
45
|
+
| 'supportsFunctionBreakpoints'
|
|
46
|
+
| 'supportsDataBreakpoints'
|
|
47
|
+
| 'supportsInstructionBreakpoints'
|
|
48
|
+
>>;
|
|
49
|
+
|
|
50
|
+
export interface BPSessionData extends BPCapabilities, DebugProtocol.Breakpoint {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint> implements TreeElement {
|
|
46
55
|
|
|
47
|
-
readonly
|
|
56
|
+
readonly labelProvider: LabelProvider;
|
|
57
|
+
readonly breakpoints: BreakpointManager;
|
|
58
|
+
readonly openerService: OpenerService;
|
|
59
|
+
readonly commandService: CommandService;
|
|
60
|
+
|
|
61
|
+
protected _raw?: BPSessionData;
|
|
62
|
+
protected readonly sessionData = new Map<string, BPSessionData>();
|
|
48
63
|
protected treeWidget?: TreeWidget;
|
|
49
64
|
|
|
50
65
|
constructor(
|
|
51
66
|
readonly uri: URI,
|
|
52
67
|
options: DebugBreakpointOptions
|
|
53
68
|
) {
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
this.labelProvider = options.labelProvider;
|
|
70
|
+
this.breakpoints = options.breakpoints;
|
|
71
|
+
this.openerService = options.openerService;
|
|
72
|
+
this.commandService = options.commandService;
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
abstract get origin(): T;
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
get raw(): BPSessionData | undefined {
|
|
78
|
+
return this._raw;
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
update(sessionId: string, data?: Omit<BPSessionData, 'sessionId'>): boolean {
|
|
82
|
+
if (!data) {
|
|
83
|
+
if (!this.sessionData.has(sessionId)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
this.sessionData.delete(sessionId);
|
|
87
|
+
} else {
|
|
88
|
+
const toSet = { ...data, sessionId };
|
|
89
|
+
this.sessionData.set(sessionId, toSet);
|
|
90
|
+
}
|
|
91
|
+
const verifiedLocations = new Map<string, BPSessionData>();
|
|
92
|
+
this.sessionData.forEach(bp => bp.verified && verifiedLocations.set(`${bp.line}:${bp.column}:${bp.instructionReference}`, bp));
|
|
93
|
+
if (verifiedLocations.size === 1) {
|
|
94
|
+
// Exactly one verified location across all sessions — use it.
|
|
95
|
+
this._raw = verifiedLocations.values().next().value;
|
|
96
|
+
} else if (verifiedLocations.size === 0) {
|
|
97
|
+
// No session has verified; pick the first session's data (if any) so
|
|
98
|
+
// that capability flags and unverified messages are still available.
|
|
99
|
+
this._raw = this.sessionData.values().next().value;
|
|
100
|
+
} else {
|
|
101
|
+
// Multiple sessions verified at different locations. Following
|
|
102
|
+
// VSCode, we set _raw to undefined so that the breakpoint falls
|
|
103
|
+
// back to its user-set position and shows as verified (the
|
|
104
|
+
// default when no resolved data exists). Callers that need a
|
|
105
|
+
// specific session's view can use getDebugProtocolBreakpoint().
|
|
106
|
+
this._raw = undefined;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getIdForSession(sessionId: string): number | undefined {
|
|
112
|
+
return this.sessionData.get(sessionId)?.id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Copied from https://github.com/microsoft/vscode/blob/8934b59d4aa696b6f51ac9bf2eeae8bbac5dac03/src/vs/workbench/contrib/debug/common/debugModel.ts#L953-L971 */
|
|
116
|
+
getDebugProtocolBreakpoint(
|
|
117
|
+
sessionId: string,
|
|
118
|
+
): DebugProtocol.Breakpoint | undefined {
|
|
119
|
+
const data = this.sessionData.get(sessionId);
|
|
120
|
+
if (data) {
|
|
121
|
+
const bp: DebugProtocol.Breakpoint = {
|
|
122
|
+
id: data.id,
|
|
123
|
+
verified: data.verified,
|
|
124
|
+
message: data.message,
|
|
125
|
+
source: data.source,
|
|
126
|
+
line: data.line,
|
|
127
|
+
column: data.column,
|
|
128
|
+
endLine: data.endLine,
|
|
129
|
+
endColumn: data.endColumn,
|
|
130
|
+
instructionReference: data.instructionReference,
|
|
131
|
+
offset: data.offset,
|
|
132
|
+
};
|
|
133
|
+
return bp;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
66
136
|
}
|
|
67
137
|
|
|
68
138
|
get id(): string {
|
|
@@ -73,12 +143,23 @@ export abstract class DebugBreakpoint<T extends BaseBreakpoint = BaseBreakpoint>
|
|
|
73
143
|
return this.breakpoints.breakpointsEnabled && this.origin.enabled;
|
|
74
144
|
}
|
|
75
145
|
|
|
146
|
+
/**
|
|
147
|
+
* True when at least one session has sent data for this breakpoint
|
|
148
|
+
* (regardless of whether it was verified).
|
|
149
|
+
*/
|
|
76
150
|
get installed(): boolean {
|
|
77
|
-
return
|
|
151
|
+
return this.sessionData.size > 0;
|
|
78
152
|
}
|
|
79
153
|
|
|
154
|
+
/**
|
|
155
|
+
* When resolved session data exists, reflects the adapter's answer.
|
|
156
|
+
* Otherwise returns `true`: either no session has weighed in yet (we
|
|
157
|
+
* haven't been told otherwise) or multiple sessions verified at
|
|
158
|
+
* different locations (`_raw` was cleared to fall back to the user-set
|
|
159
|
+
* position). Matches VSCode's default-true semantics.
|
|
160
|
+
*/
|
|
80
161
|
get verified(): boolean {
|
|
81
|
-
return
|
|
162
|
+
return this._raw ? this._raw.verified : true;
|
|
82
163
|
}
|
|
83
164
|
|
|
84
165
|
get message(): string {
|
|
@@ -22,14 +22,17 @@ import { DataBreakpoint } from '../breakpoint/breakpoint-marker';
|
|
|
22
22
|
import { DebugBreakpoint, DebugBreakpointDecoration, DebugBreakpointOptions } from './debug-breakpoint';
|
|
23
23
|
|
|
24
24
|
export class DebugDataBreakpoint extends DebugBreakpoint<DataBreakpoint> {
|
|
25
|
+
|
|
26
|
+
static create(origin: DataBreakpoint, options: DebugBreakpointOptions): DebugDataBreakpoint {
|
|
27
|
+
return new this(origin, options);
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
constructor(readonly origin: DataBreakpoint, options: DebugBreakpointOptions) {
|
|
26
31
|
super(BreakpointManager.DATA_URI, options);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
setEnabled(enabled: boolean): void {
|
|
30
|
-
|
|
31
|
-
this.breakpoints.updateDataBreakpoint(this.origin.id, { enabled });
|
|
32
|
-
}
|
|
35
|
+
this.breakpoints.enableBreakpoint(this, enabled);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
protected override isEnabled(): boolean {
|
|
@@ -37,11 +40,11 @@ export class DebugDataBreakpoint extends DebugBreakpoint<DataBreakpoint> {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
protected isSupported(): boolean {
|
|
40
|
-
return
|
|
43
|
+
return this.raw ? !!this.raw.supportsDataBreakpoints : true;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
remove(): void {
|
|
44
|
-
this.breakpoints.removeDataBreakpoint(this
|
|
47
|
+
this.breakpoints.removeDataBreakpoint(this);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
protected doRender(): React.ReactNode {
|
|
@@ -25,17 +25,16 @@ import { codicon } from '@theia/core/lib/browser';
|
|
|
25
25
|
|
|
26
26
|
export class DebugFunctionBreakpoint extends DebugBreakpoint<FunctionBreakpoint> implements TreeElement {
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
static create(origin: FunctionBreakpoint, options: DebugBreakpointOptions): DebugFunctionBreakpoint {
|
|
29
|
+
return new this(origin, options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private constructor(readonly origin: FunctionBreakpoint, options: DebugBreakpointOptions) {
|
|
29
33
|
super(BreakpointManager.FUNCTION_URI, options);
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
setEnabled(enabled: boolean): void {
|
|
33
|
-
|
|
34
|
-
const breakpoint = breakpoints.find(b => b.id === this.id);
|
|
35
|
-
if (breakpoint && breakpoint.enabled !== enabled) {
|
|
36
|
-
breakpoint.enabled = enabled;
|
|
37
|
-
this.breakpoints.setFunctionBreakpoints(breakpoints);
|
|
38
|
-
}
|
|
37
|
+
this.breakpoints.enableBreakpoint(this, enabled);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
protected override isEnabled(): boolean {
|
|
@@ -43,16 +42,11 @@ export class DebugFunctionBreakpoint extends DebugBreakpoint<FunctionBreakpoint>
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
protected isSupported(): boolean {
|
|
46
|
-
|
|
47
|
-
return !session || !!session.capabilities.supportsFunctionBreakpoints;
|
|
45
|
+
return this.raw ? !!this.raw.supportsFunctionBreakpoints : true;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
remove(): void {
|
|
51
|
-
|
|
52
|
-
const newBreakpoints = breakpoints.filter(b => b.id !== this.id);
|
|
53
|
-
if (breakpoints.length !== newBreakpoints.length) {
|
|
54
|
-
this.breakpoints.setFunctionBreakpoints(newBreakpoints);
|
|
55
|
-
}
|
|
49
|
+
this.breakpoints.removeFunctionBreakpoint(this);
|
|
56
50
|
}
|
|
57
51
|
|
|
58
52
|
get name(): string {
|
|
@@ -97,26 +91,21 @@ export class DebugFunctionBreakpoint extends DebugBreakpoint<FunctionBreakpoint>
|
|
|
97
91
|
};
|
|
98
92
|
}
|
|
99
93
|
|
|
100
|
-
async
|
|
94
|
+
static async editOrCreate(breakpoints: BreakpointManager, existing?: DebugFunctionBreakpoint): Promise<void> {
|
|
101
95
|
const input = new SingleTextInputDialog({
|
|
102
96
|
title: nls.localizeByDefault('Add Function Breakpoint'),
|
|
103
|
-
initialValue:
|
|
97
|
+
initialValue: existing?.name ?? ''
|
|
104
98
|
});
|
|
105
99
|
const newValue = await input.open();
|
|
106
|
-
if (newValue
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
breakpoint.raw.name = newValue;
|
|
112
|
-
this.breakpoints.setFunctionBreakpoints(breakpoints);
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
this.origin.raw.name = newValue;
|
|
116
|
-
breakpoints.push(this.origin);
|
|
117
|
-
this.breakpoints.setFunctionBreakpoints(breakpoints);
|
|
118
|
-
}
|
|
100
|
+
if (!newValue) { return; }
|
|
101
|
+
if (existing) {
|
|
102
|
+
breakpoints.updateFunctionBreakpoint(existing, { name: newValue });
|
|
103
|
+
} else {
|
|
104
|
+
breakpoints.addFunctionBreakpoint(FunctionBreakpoint.create({ name: newValue }));
|
|
119
105
|
}
|
|
120
106
|
}
|
|
121
107
|
|
|
108
|
+
async open(): Promise<void> {
|
|
109
|
+
DebugFunctionBreakpoint.editOrCreate(this.breakpoints, this);
|
|
110
|
+
}
|
|
122
111
|
}
|
|
@@ -22,14 +22,16 @@ import { InstructionBreakpoint } from '../breakpoint/breakpoint-marker';
|
|
|
22
22
|
import { DebugBreakpoint, DebugBreakpointDecoration, DebugBreakpointOptions } from './debug-breakpoint';
|
|
23
23
|
|
|
24
24
|
export class DebugInstructionBreakpoint extends DebugBreakpoint<InstructionBreakpoint> {
|
|
25
|
-
|
|
25
|
+
static create(origin: InstructionBreakpoint, options: DebugBreakpointOptions): DebugInstructionBreakpoint {
|
|
26
|
+
return new this(origin, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private constructor(readonly origin: InstructionBreakpoint, options: DebugBreakpointOptions) {
|
|
26
30
|
super(BreakpointManager.INSTRUCTION_URI, options);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
setEnabled(enabled: boolean): void {
|
|
30
|
-
|
|
31
|
-
this.breakpoints.updateInstructionBreakpoint(this.origin.id, { enabled });
|
|
32
|
-
}
|
|
34
|
+
this.breakpoints.enableBreakpoint(this, enabled);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
protected override isEnabled(): boolean {
|
|
@@ -37,16 +39,16 @@ export class DebugInstructionBreakpoint extends DebugBreakpoint<InstructionBreak
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
protected isSupported(): boolean {
|
|
40
|
-
return
|
|
42
|
+
return this.raw ? !!this.raw.supportsInstructionBreakpoints : true;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
remove(): void {
|
|
44
|
-
this.breakpoints.removeInstructionBreakpoint(this
|
|
46
|
+
this.breakpoints.removeInstructionBreakpoint(this);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
protected doRender(): React.ReactNode {
|
|
48
50
|
return <React.Fragment>
|
|
49
|
-
<span className="line-info">{this.origin.instructionReference}</span
|
|
51
|
+
<span className="line-info">{this.origin.raw.instructionReference}</span>
|
|
50
52
|
{this.renderActions()}
|
|
51
53
|
</React.Fragment>;
|
|
52
54
|
}
|
|
@@ -69,7 +71,7 @@ export class DebugInstructionBreakpoint extends DebugBreakpoint<InstructionBreak
|
|
|
69
71
|
message: message ?? [nls.localize('theia/debug/instruction-breakpoint', 'Instruction Breakpoint')],
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
|
-
if (this.origin.condition || this.origin.hitCondition) {
|
|
74
|
+
if (this.origin.raw.condition || this.origin.raw.hitCondition) {
|
|
73
75
|
return {
|
|
74
76
|
className: 'codicon-debug-breakpoint-conditional',
|
|
75
77
|
message: message || [nls.localize('theia/debug/conditionalBreakpoint', 'Conditional Breakpoint')]
|