提交 6980e4e2 编写于 作者: I isidor

Implement the DAP Invalidate request

fixes #106745
上级 5ed27688
......@@ -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';
......@@ -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>();
......@@ -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;
......@@ -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) {
......@@ -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)));
}
......@@ -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 {
......
......@@ -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>();
......@@ -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;
......@@ -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;
}
......
......@@ -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';
......@@ -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;
......@@ -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>();
......@@ -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;
......@@ -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();
......@@ -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;
......@@ -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();
});
}
}
......
......@@ -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';
......@@ -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>;
......@@ -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);
......@@ -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;
......@@ -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());
}
......
......@@ -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 {
......
......@@ -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>;
......@@ -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);
......
......@@ -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';
......@@ -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 } {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册