Skip to content

Commit

Permalink
Implement the DAP Invalidate request
Browse files Browse the repository at this point in the history
  • Loading branch information
isidorn authored and meganrogge committed Sep 22, 2020
1 parent 56181af commit 0d2a5ff
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 25 deletions.
21 changes: 18 additions & 3 deletions src/vs/workbench/contrib/debug/browser/debugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
Expand All @@ -52,6 +51,7 @@ export class DebugSession implements IDebugSession {
private rawListeners: IDisposable[] = [];
private fetchThreadsScheduler: RunOnceScheduler | undefined;
private repl: ReplModel;
private stoppedDetails: IRawStoppedDetails | undefined;

private readonly _onDidChangeState = new Emitter<void>();
private readonly _onDidEndAdapter = new Emitter<AdapterEndEvent | undefined>();
Expand Down Expand Up @@ -247,7 +247,8 @@ export class DebugSession implements IDebugSession {
supportsVariablePaging: true, // #9537
supportsRunInTerminalRequest: true, // #10574
locale: platform.locale,
supportsProgressReporting: true // #92253
supportsProgressReporting: true, // #92253
supportsInvalidatedEvent: true // #106745
});

this.initialized = true;
Expand Down Expand Up @@ -800,6 +801,7 @@ export class DebugSession implements IDebugSession {
}));

this.rawListeners.push(this.raw.onDidStop(async event => {
this.stoppedDetails = event.body;
await this.fetchThreads(event.body);
const thread = typeof event.body.threadId === 'number' ? this.getThread(event.body.threadId) : undefined;
if (thread) {
Expand Down Expand Up @@ -1001,6 +1003,19 @@ export class DebugSession implements IDebugSession {
this.rawListeners.push(this.raw.onDidProgressEnd(event => {
this._onDidProgressEnd.fire(event);
}));
this.rawListeners.push(this.raw.onDidInvalidated(async event => {
if (!(event.body.areas && event.body.areas.length === 1 && event.body.areas[0] === 'variables')) {
// If invalidated event only requires to update variables, do that, otherwise refatch threads https://github.com/microsoft/vscode/issues/106745
this.cancelAllRequests();
this.model.clearThreads(this.getId(), true);
await this.fetchThreads(this.stoppedDetails);
}

const viewModel = this.debugService.getViewModel();
if (viewModel.focusedSession === this) {
viewModel.updateViews();
}
}));

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

appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {
Expand Down
8 changes: 8 additions & 0 deletions src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class RawDebugSession implements IDisposable {
private readonly _onDidProgressStart = new Emitter<DebugProtocol.ProgressStartEvent>();
private readonly _onDidProgressUpdate = new Emitter<DebugProtocol.ProgressUpdateEvent>();
private readonly _onDidProgressEnd = new Emitter<DebugProtocol.ProgressEndEvent>();
private readonly _onDidInvalidated = new Emitter<DebugProtocol.InvalidatedEvent>();
private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();
private readonly _onDidEvent = new Emitter<DebugProtocol.Event>();

Expand Down Expand Up @@ -150,6 +151,9 @@ export class RawDebugSession implements IDisposable {
case 'progressEnd':
this._onDidProgressEnd.fire(event as DebugProtocol.ProgressEndEvent);
break;
case 'invalidated':
this._onDidInvalidated.fire(event as DebugProtocol.InvalidatedEvent);
break;
default:
this._onDidCustomEvent.fire(event);
break;
Expand Down Expand Up @@ -230,6 +234,10 @@ export class RawDebugSession implements IDisposable {
return this._onDidProgressEnd.event;
}

get onDidInvalidated(): Event<DebugProtocol.InvalidatedEvent> {
return this._onDidInvalidated.event;
}

get onDidEvent(): Event<DebugProtocol.Event> {
return this._onDidEvent.event;
}
Expand Down
14 changes: 6 additions & 8 deletions src/vs/workbench/contrib/debug/browser/variablesView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { Emitter } from 'vs/base/common/event';
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
Expand All @@ -41,7 +40,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
const $ = dom.$;
let forgetScopes = true;

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

Expand All @@ -52,7 +50,7 @@ interface IVariablesContext {

export class VariablesView extends ViewPane {

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

// Use scheduler to prevent unnecessary flashing
this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => {
this.updateTreeScheduler = new RunOnceScheduler(async () => {
const stackFrame = this.debugService.getViewModel().focusedStackFrame;

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

this._register(this.onDidChangeBodyVisibility(visible => {
if (visible && this.needsRefresh) {
this.onFocusStackFrameScheduler.schedule();
this.updateTreeScheduler.schedule();
}
}));
let horizontalScrolling: boolean | undefined;
Expand Down Expand Up @@ -358,7 +356,7 @@ export class VariablesRenderer extends AbstractExpressionsRenderer {
.then(() => {
// Do not refresh scopes due to a node limitation #15520
forgetScopes = false;
variableSetEmitter.fire();
this.debugService.getViewModel().updateViews();
});
}
}
Expand Down
24 changes: 12 additions & 12 deletions src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
import { FuzzyScore } from 'vs/base/common/filters';
import { IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { variableSetEmitter, VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { dispose } from 'vs/base/common/lifecycle';
import { IViewDescriptorService } from 'vs/workbench/common/views';
Expand All @@ -34,12 +34,12 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
let ignoreVariableSetEmitter = false;
let ignoreViewUpdates = false;
let useCachedEvaluation = false;

export class WatchExpressionsView extends ViewPane {

private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
private watchExpressionsUpdatedScheduler: RunOnceScheduler;
private needsRefresh = false;
private tree!: WorkbenchAsyncDataTree<IDebugService | IExpression, IExpression, FuzzyScore>;

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

this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
this.watchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
this.needsRefresh = false;
this.tree.updateChildren();
}, 50);
Expand Down Expand Up @@ -117,19 +117,19 @@ export class WatchExpressionsView extends ViewPane {
return;
}

if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
this.onWatchExpressionsUpdatedScheduler.schedule();
if (!this.watchExpressionsUpdatedScheduler.isScheduled()) {
this.watchExpressionsUpdatedScheduler.schedule();
}
}));
this._register(variableSetEmitter.event(() => {
if (!ignoreVariableSetEmitter) {
this._register(this.debugService.getViewModel().onWillUpdateViews(() => {
if (!ignoreViewUpdates) {
this.tree.updateChildren();
}
}));

this._register(this.onDidChangeBodyVisibility(visible => {
if (visible && this.needsRefresh) {
this.onWatchExpressionsUpdatedScheduler.schedule();
this.watchExpressionsUpdatedScheduler.schedule();
}
}));
let horizontalScrolling: boolean | undefined;
Expand Down Expand Up @@ -291,9 +291,9 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer {
onFinish: (value: string, success: boolean) => {
if (success && value) {
this.debugService.renameWatchExpression(expression.getId(), value);
ignoreVariableSetEmitter = true;
variableSetEmitter.fire();
ignoreVariableSetEmitter = false;
ignoreViewUpdates = true;
this.debugService.getViewModel().updateViews();
ignoreViewUpdates = false;
} else if (!expression.name) {
this.debugService.removeWatchExpressions(expression.getId());
}
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/contrib/debug/common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,14 @@ export interface IViewModel extends ITreeElement {
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined;
setSelectedExpression(expression: IExpression | undefined): void;
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void;
updateViews(): void;

isMultiSessionView(): boolean;

onDidFocusSession: Event<IDebugSession | undefined>;
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }>;
onDidSelectExpression: Event<IExpression | undefined>;
onWillUpdateViews: Event<void>;
}

export interface IEvaluate {
Expand Down
9 changes: 9 additions & 0 deletions src/vs/workbench/contrib/debug/common/debugViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class ViewModel implements IViewModel {
private readonly _onDidFocusSession = new Emitter<IDebugSession | undefined>();
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>();
private readonly _onDidSelectExpression = new Emitter<IExpression | undefined>();
private readonly _onWillUpdateViews = new Emitter<void>();
private multiSessionView: boolean;
private expressionSelectedContextKey!: IContextKey<boolean>;
private breakpointSelectedContextKey!: IContextKey<boolean>;
Expand Down Expand Up @@ -115,6 +116,14 @@ export class ViewModel implements IViewModel {
return this.selectedFunctionBreakpoint;
}

updateViews(): void {
this._onWillUpdateViews.fire();
}

get onWillUpdateViews(): Event<void> {
return this._onWillUpdateViews.event;
}

setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void {
this.selectedFunctionBreakpoint = functionBreakpoint;
this.breakpointSelectedContextKey.set(!!functionBreakpoint);
Expand Down
12 changes: 10 additions & 2 deletions src/vs/workbench/contrib/debug/test/browser/callStack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { MockRawSession, createMockDebugModel, mockUriIdentityService } from 'vs
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { Range } from 'vs/editor/common/core/range';
import { IDebugSessionOptions, State } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugSessionOptions, State, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { NullOpenerService } from 'vs/platform/opener/common/opener';
import { createDecorationsForStackFrame } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
import { Constants } from 'vs/base/common/uint';
Expand All @@ -19,7 +19,15 @@ import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug
import { generateUuid } from 'vs/base/common/uuid';

export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
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);
return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, {
getViewModel(): any {
return {
updateViews(): void {
// noop
}
};
}
} as IDebugService, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!, mockUriIdentityService);
}

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

0 comments on commit 0d2a5ff

Please sign in to comment.