Skip to content

Commit 18fc2bc

Browse files
committed
variables view: improvements to view state preservation
microsoft#93230
1 parent ebc9564 commit 18fc2bc

2 files changed

Lines changed: 36 additions & 37 deletions

File tree

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

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { RunOnceScheduler } from 'vs/base/common/async';
88
import * as dom from 'vs/base/browser/dom';
99
import { CollapseAction } from 'vs/workbench/browser/viewlet';
1010
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
11-
import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IViewModel } from 'vs/workbench/contrib/debug/common/debug';
12-
import { Variable, Scope, ErrorScope } from 'vs/workbench/contrib/debug/common/debugModel';
11+
import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IStackFrame } from 'vs/workbench/contrib/debug/common/debug';
12+
import { Variable, Scope, ErrorScope, StackFrame } from 'vs/workbench/contrib/debug/common/debugModel';
1313
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1414
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1515
import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView';
@@ -34,6 +34,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views';
3434
import { IOpenerService } from 'vs/platform/opener/common/opener';
3535
import { IThemeService } from 'vs/platform/theme/common/themeService';
3636
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
37+
import { withUndefinedAsNull } from 'vs/base/common/types';
3738

3839
const $ = dom.$;
3940
let forgetScopes = true;
@@ -44,8 +45,8 @@ export class VariablesView extends ViewPane {
4445

4546
private onFocusStackFrameScheduler: RunOnceScheduler;
4647
private needsRefresh = false;
47-
private tree!: WorkbenchAsyncDataTree<IViewModel | IExpression | IScope, IExpression | IScope, FuzzyScore>;
48-
private savedViewState: IAsyncDataTreeViewState | undefined;
48+
private tree!: WorkbenchAsyncDataTree<IStackFrame | null, IExpression | IScope, FuzzyScore>;
49+
private savedViewState = new Map<string, IAsyncDataTreeViewState>();
4950

5051
constructor(
5152
options: IViewletViewOptions,
@@ -68,26 +69,23 @@ export class VariablesView extends ViewPane {
6869
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
6970

7071
this.needsRefresh = false;
71-
if (stackFrame && this.savedViewState) {
72-
await this.tree.setInput(this.debugService.getViewModel(), this.savedViewState);
73-
this.savedViewState = undefined;
74-
} else {
75-
if (!stackFrame) {
76-
// We have no stackFrame, save tree state before it is cleared
77-
this.savedViewState = this.tree.getViewState();
78-
}
79-
await this.tree.updateChildren();
80-
if (stackFrame) {
81-
const scopes = await stackFrame.getScopes();
82-
// Expand the first scope if it is not expensive and if there is no expansion state (all are collapsed)
83-
if (scopes.every(s => this.tree.getNode(s).collapsed) && scopes.length > 0) {
84-
const toExpand = scopes.find(s => !s.expensive);
85-
if (toExpand) {
86-
this.tree.expand(toExpand);
87-
}
88-
}
89-
}
72+
const input = this.tree.getInput();
73+
if (input) {
74+
this.savedViewState.set(input.getId(), this.tree.getViewState());
75+
}
76+
if (!stackFrame) {
77+
await this.tree.setInput(null);
78+
return;
79+
}
80+
81+
const viewState = this.savedViewState.get(stackFrame.getId());
82+
await this.tree.setInput(stackFrame, viewState);
9083

84+
// Automatically expand the first scope if it is not expensive and if all scopes are collapsed
85+
const scopes = await stackFrame.getScopes();
86+
const toExpand = scopes.find(s => !s.expensive);
87+
if (toExpand && scopes.every(s => this.tree.isCollapsed(s))) {
88+
await this.tree.expand(toExpand);
9189
}
9290
}, 400);
9391
}
@@ -99,7 +97,7 @@ export class VariablesView extends ViewPane {
9997
dom.addClass(container, 'debug-variables');
10098
const treeContainer = renderViewTree(container);
10199

102-
this.tree = <WorkbenchAsyncDataTree<IViewModel | IExpression | IScope, IExpression | IScope, FuzzyScore>>this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'VariablesView', treeContainer, new VariablesDelegate(),
100+
this.tree = <WorkbenchAsyncDataTree<IStackFrame | null, IExpression | IScope, FuzzyScore>>this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'VariablesView', treeContainer, new VariablesDelegate(),
103101
[this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer(), new ScopeErrorRenderer()],
104102
new VariablesDataSource(), {
105103
accessibilityProvider: new VariablesAccessibilityProvider(),
@@ -110,12 +108,10 @@ export class VariablesView extends ViewPane {
110108
}
111109
});
112110

113-
this.tree.setInput(this.debugService.getViewModel());
111+
this.tree.setInput(withUndefinedAsNull(this.debugService.getViewModel().focusedStackFrame));
114112

115113
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
116114

117-
this.tree.updateChildren();
118-
119115
this._register(this.debugService.getViewModel().onDidFocusStackFrame(sf => {
120116
if (!this.isBodyVisible()) {
121117
this.needsRefresh = true;
@@ -148,6 +144,7 @@ export class VariablesView extends ViewPane {
148144
this.tree.rerender(e);
149145
}
150146
}));
147+
this._register(this.debugService.onDidEndSession(() => this.savedViewState.clear()));
151148
}
152149

153150
getActions(): IAction[] {
@@ -213,24 +210,26 @@ export class VariablesView extends ViewPane {
213210
}
214211
}
215212

216-
function isViewModel(obj: any): obj is IViewModel {
217-
return typeof obj.getSelectedExpression === 'function';
213+
function isStackFrame(obj: any): obj is IStackFrame {
214+
return obj instanceof StackFrame;
218215
}
219216

220-
export class VariablesDataSource implements IAsyncDataSource<IViewModel, IExpression | IScope> {
217+
export class VariablesDataSource implements IAsyncDataSource<IStackFrame | null, IExpression | IScope> {
221218

222-
hasChildren(element: IViewModel | IExpression | IScope): boolean {
223-
if (isViewModel(element)) {
219+
hasChildren(element: IStackFrame | null | IExpression | IScope): boolean {
220+
if (!element) {
221+
return false;
222+
}
223+
if (isStackFrame(element)) {
224224
return true;
225225
}
226226

227227
return element.hasChildren;
228228
}
229229

230-
getChildren(element: IViewModel | IExpression | IScope): Promise<(IExpression | IScope)[]> {
231-
if (isViewModel(element)) {
232-
const stackFrame = element.focusedStackFrame;
233-
return stackFrame ? stackFrame.getScopes() : Promise.resolve([]);
230+
getChildren(element: IStackFrame | IExpression | IScope): Promise<(IExpression | IScope)[]> {
231+
if (isStackFrame(element)) {
232+
return element.getScopes();
234233
}
235234

236235
return element.getChildren();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ export class StackFrame implements IStackFrame {
298298
) { }
299299

300300
getId(): string {
301-
return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`;
301+
return `stackframe:${this.thread.getId()}:${this.index}:${this.source.name}`;
302302
}
303303

304304
getScopes(): Promise<IScope[]> {

0 commit comments

Comments
 (0)