Skip to content

Commit 6980e4e

Browse files
committed
Implement the DAP Invalidate request
fixes microsoft#106745
1 parent 5ed2768 commit 6980e4e

7 files changed

Lines changed: 65 additions & 25 deletions

File tree

src/vs/workbench/contrib/debug/browser/debugSession.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
2828
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
2929
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
3030
import { IOpenerService } from 'vs/platform/opener/common/opener';
31-
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
3231
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
3332
import { distinct } from 'vs/base/common/arrays';
3433
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -52,6 +51,7 @@ export class DebugSession implements IDebugSession {
5251
private rawListeners: IDisposable[] = [];
5352
private fetchThreadsScheduler: RunOnceScheduler | undefined;
5453
private repl: ReplModel;
54+
private stoppedDetails: IRawStoppedDetails | undefined;
5555

5656
private readonly _onDidChangeState = new Emitter<void>();
5757
private readonly _onDidEndAdapter = new Emitter<AdapterEndEvent | undefined>();
@@ -247,7 +247,8 @@ export class DebugSession implements IDebugSession {
247247
supportsVariablePaging: true, // #9537
248248
supportsRunInTerminalRequest: true, // #10574
249249
locale: platform.locale,
250-
supportsProgressReporting: true // #92253
250+
supportsProgressReporting: true, // #92253
251+
supportsInvalidatedEvent: true // #106745
251252
});
252253

253254
this.initialized = true;
@@ -800,6 +801,7 @@ export class DebugSession implements IDebugSession {
800801
}));
801802

802803
this.rawListeners.push(this.raw.onDidStop(async event => {
804+
this.stoppedDetails = event.body;
803805
await this.fetchThreads(event.body);
804806
const thread = typeof event.body.threadId === 'number' ? this.getThread(event.body.threadId) : undefined;
805807
if (thread) {
@@ -1001,6 +1003,19 @@ export class DebugSession implements IDebugSession {
10011003
this.rawListeners.push(this.raw.onDidProgressEnd(event => {
10021004
this._onDidProgressEnd.fire(event);
10031005
}));
1006+
this.rawListeners.push(this.raw.onDidInvalidated(async event => {
1007+
if (!(event.body.areas && event.body.areas.length === 1 && event.body.areas[0] === 'variables')) {
1008+
// If invalidated event only requires to update variables, do that, otherwise refatch threads https://github.com/microsoft/vscode/issues/106745
1009+
this.cancelAllRequests();
1010+
this.model.clearThreads(this.getId(), true);
1011+
await this.fetchThreads(this.stoppedDetails);
1012+
}
1013+
1014+
const viewModel = this.debugService.getViewModel();
1015+
if (viewModel.focusedSession === this) {
1016+
viewModel.updateViews();
1017+
}
1018+
}));
10041019

10051020
this.rawListeners.push(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event)));
10061021
}
@@ -1091,7 +1106,7 @@ export class DebugSession implements IDebugSession {
10911106
async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
10921107
await this.repl.addReplExpression(this, stackFrame, name);
10931108
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
1094-
variableSetEmitter.fire();
1109+
this.debugService.getViewModel().updateViews();
10951110
}
10961111

10971112
appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {

src/vs/workbench/contrib/debug/browser/rawDebugSession.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export class RawDebugSession implements IDisposable {
6969
private readonly _onDidProgressStart = new Emitter<DebugProtocol.ProgressStartEvent>();
7070
private readonly _onDidProgressUpdate = new Emitter<DebugProtocol.ProgressUpdateEvent>();
7171
private readonly _onDidProgressEnd = new Emitter<DebugProtocol.ProgressEndEvent>();
72+
private readonly _onDidInvalidated = new Emitter<DebugProtocol.InvalidatedEvent>();
7273
private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();
7374
private readonly _onDidEvent = new Emitter<DebugProtocol.Event>();
7475

@@ -150,6 +151,9 @@ export class RawDebugSession implements IDisposable {
150151
case 'progressEnd':
151152
this._onDidProgressEnd.fire(event as DebugProtocol.ProgressEndEvent);
152153
break;
154+
case 'invalidated':
155+
this._onDidInvalidated.fire(event as DebugProtocol.InvalidatedEvent);
156+
break;
153157
default:
154158
this._onDidCustomEvent.fire(event);
155159
break;
@@ -230,6 +234,10 @@ export class RawDebugSession implements IDisposable {
230234
return this._onDidProgressEnd.event;
231235
}
232236

237+
get onDidInvalidated(): Event<DebugProtocol.InvalidatedEvent> {
238+
return this._onDidInvalidated.event;
239+
}
240+
233241
get onDidEvent(): Event<DebugProtocol.Event> {
234242
return this._onDidEvent.event;
235243
}

src/vs/workbench/contrib/debug/browser/variablesView.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
2121
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
2222
import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
2323
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
24-
import { Emitter } from 'vs/base/common/event';
2524
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
2625
import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
2726
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
@@ -41,7 +40,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
4140
const $ = dom.$;
4241
let forgetScopes = true;
4342

44-
export const variableSetEmitter = new Emitter<void>();
4543
let variableInternalContext: Variable | undefined;
4644
let dataBreakpointInfoResponse: IDataBreakpointInfoResponse | undefined;
4745

@@ -52,7 +50,7 @@ interface IVariablesContext {
5250

5351
export class VariablesView extends ViewPane {
5452

55-
private onFocusStackFrameScheduler: RunOnceScheduler;
53+
private updateTreeScheduler: RunOnceScheduler;
5654
private needsRefresh = false;
5755
private tree!: WorkbenchAsyncDataTree<IStackFrame | null, IExpression | IScope, FuzzyScore>;
5856
private savedViewState = new Map<string, IAsyncDataTreeViewState>();
@@ -85,7 +83,7 @@ export class VariablesView extends ViewPane {
8583
this.variableEvaluateName = CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT.bindTo(contextKeyService);
8684

8785
// Use scheduler to prevent unnecessary flashing
88-
this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => {
86+
this.updateTreeScheduler = new RunOnceScheduler(async () => {
8987
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
9088

9189
this.needsRefresh = false;
@@ -142,9 +140,9 @@ export class VariablesView extends ViewPane {
142140
// Refresh the tree immediately if the user explictly changed stack frames.
143141
// Otherwise postpone the refresh until user stops stepping.
144142
const timeout = sf.explicit ? 0 : undefined;
145-
this.onFocusStackFrameScheduler.schedule(timeout);
143+
this.updateTreeScheduler.schedule(timeout);
146144
}));
147-
this._register(variableSetEmitter.event(() => {
145+
this._register(this.debugService.getViewModel().onWillUpdateViews(() => {
148146
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
149147
if (stackFrame && forgetScopes) {
150148
stackFrame.forgetScopes();
@@ -157,7 +155,7 @@ export class VariablesView extends ViewPane {
157155

158156
this._register(this.onDidChangeBodyVisibility(visible => {
159157
if (visible && this.needsRefresh) {
160-
this.onFocusStackFrameScheduler.schedule();
158+
this.updateTreeScheduler.schedule();
161159
}
162160
}));
163161
let horizontalScrolling: boolean | undefined;
@@ -358,7 +356,7 @@ export class VariablesRenderer extends AbstractExpressionsRenderer {
358356
.then(() => {
359357
// Do not refresh scopes due to a node limitation #15520
360358
forgetScopes = false;
361-
variableSetEmitter.fire();
359+
this.debugService.getViewModel().updateViews();
362360
});
363361
}
364362
}

src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
2525
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
2626
import { FuzzyScore } from 'vs/base/common/filters';
2727
import { IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
28-
import { variableSetEmitter, VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
28+
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
2929
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
3030
import { dispose } from 'vs/base/common/lifecycle';
3131
import { IViewDescriptorService } from 'vs/workbench/common/views';
@@ -34,12 +34,12 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
3434
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
3535

3636
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
37-
let ignoreVariableSetEmitter = false;
37+
let ignoreViewUpdates = false;
3838
let useCachedEvaluation = false;
3939

4040
export class WatchExpressionsView extends ViewPane {
4141

42-
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
42+
private watchExpressionsUpdatedScheduler: RunOnceScheduler;
4343
private needsRefresh = false;
4444
private tree!: WorkbenchAsyncDataTree<IDebugService | IExpression, IExpression, FuzzyScore>;
4545

@@ -58,7 +58,7 @@ export class WatchExpressionsView extends ViewPane {
5858
) {
5959
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
6060

61-
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
61+
this.watchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
6262
this.needsRefresh = false;
6363
this.tree.updateChildren();
6464
}, 50);
@@ -117,19 +117,19 @@ export class WatchExpressionsView extends ViewPane {
117117
return;
118118
}
119119

120-
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
121-
this.onWatchExpressionsUpdatedScheduler.schedule();
120+
if (!this.watchExpressionsUpdatedScheduler.isScheduled()) {
121+
this.watchExpressionsUpdatedScheduler.schedule();
122122
}
123123
}));
124-
this._register(variableSetEmitter.event(() => {
125-
if (!ignoreVariableSetEmitter) {
124+
this._register(this.debugService.getViewModel().onWillUpdateViews(() => {
125+
if (!ignoreViewUpdates) {
126126
this.tree.updateChildren();
127127
}
128128
}));
129129

130130
this._register(this.onDidChangeBodyVisibility(visible => {
131131
if (visible && this.needsRefresh) {
132-
this.onWatchExpressionsUpdatedScheduler.schedule();
132+
this.watchExpressionsUpdatedScheduler.schedule();
133133
}
134134
}));
135135
let horizontalScrolling: boolean | undefined;
@@ -291,9 +291,9 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer {
291291
onFinish: (value: string, success: boolean) => {
292292
if (success && value) {
293293
this.debugService.renameWatchExpression(expression.getId(), value);
294-
ignoreVariableSetEmitter = true;
295-
variableSetEmitter.fire();
296-
ignoreVariableSetEmitter = false;
294+
ignoreViewUpdates = true;
295+
this.debugService.getViewModel().updateViews();
296+
ignoreViewUpdates = false;
297297
} else if (!expression.name) {
298298
this.debugService.removeWatchExpressions(expression.getId());
299299
}

src/vs/workbench/contrib/debug/common/debug.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,12 +437,14 @@ export interface IViewModel extends ITreeElement {
437437
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined;
438438
setSelectedExpression(expression: IExpression | undefined): void;
439439
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void;
440+
updateViews(): void;
440441

441442
isMultiSessionView(): boolean;
442443

443444
onDidFocusSession: Event<IDebugSession | undefined>;
444445
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }>;
445446
onDidSelectExpression: Event<IExpression | undefined>;
447+
onWillUpdateViews: Event<void>;
446448
}
447449

448450
export interface IEvaluate {

src/vs/workbench/contrib/debug/common/debugViewModel.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class ViewModel implements IViewModel {
2020
private readonly _onDidFocusSession = new Emitter<IDebugSession | undefined>();
2121
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>();
2222
private readonly _onDidSelectExpression = new Emitter<IExpression | undefined>();
23+
private readonly _onWillUpdateViews = new Emitter<void>();
2324
private multiSessionView: boolean;
2425
private expressionSelectedContextKey!: IContextKey<boolean>;
2526
private breakpointSelectedContextKey!: IContextKey<boolean>;
@@ -115,6 +116,14 @@ export class ViewModel implements IViewModel {
115116
return this.selectedFunctionBreakpoint;
116117
}
117118

119+
updateViews(): void {
120+
this._onWillUpdateViews.fire();
121+
}
122+
123+
get onWillUpdateViews(): Event<void> {
124+
return this._onWillUpdateViews.event;
125+
}
126+
118127
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void {
119128
this.selectedFunctionBreakpoint = functionBreakpoint;
120129
this.breakpointSelectedContextKey.set(!!functionBreakpoint);

src/vs/workbench/contrib/debug/test/browser/callStack.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { MockRawSession, createMockDebugModel, mockUriIdentityService } from 'vs
1010
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
1111
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
1212
import { Range } from 'vs/editor/common/core/range';
13-
import { IDebugSessionOptions, State } from 'vs/workbench/contrib/debug/common/debug';
13+
import { IDebugSessionOptions, State, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
1414
import { NullOpenerService } from 'vs/platform/opener/common/opener';
1515
import { createDecorationsForStackFrame } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
1616
import { Constants } from 'vs/base/common/uint';
@@ -19,7 +19,15 @@ import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug
1919
import { generateUuid } from 'vs/base/common/uuid';
2020

2121
export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
22-
return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!, mockUriIdentityService);
22+
return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, {
23+
getViewModel(): any {
24+
return {
25+
updateViews(): void {
26+
// noop
27+
}
28+
};
29+
}
30+
} as IDebugService, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!, mockUriIdentityService);
2331
}
2432

2533
function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFrame, secondStackFrame: StackFrame } {

0 commit comments

Comments
 (0)