Skip to content

Commit d6247d0

Browse files
committed
debug: variables and watch quit with the highlight business. Introduce selected wath context key
fixes microsoft#42753
1 parent 94115e5 commit d6247d0

9 files changed

Lines changed: 52 additions & 69 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('bre
4040
export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', true);
4141
export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', true);
4242
export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', true);
43+
export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey<boolean>('expressionSelected', false);
4344

4445
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
4546
export const DEBUG_SCHEME = 'debug';

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,10 +1048,12 @@ export class Model implements IModel {
10481048
return this.watchExpressions;
10491049
}
10501050

1051-
public addWatchExpression(process: IProcess, stackFrame: IStackFrame, name: string): void {
1051+
public addWatchExpression(process: IProcess, stackFrame: IStackFrame, name: string): IExpression {
10521052
const we = new Expression(name);
10531053
this.watchExpressions.push(we);
10541054
this._onDidChangeWatchExpressions.fire(we);
1055+
1056+
return we;
10551057
}
10561058

10571059
public renameWatchExpression(process: IProcess, stackFrame: IStackFrame, id: string, newName: string): void {

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

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,47 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import Event, { Emitter } from 'vs/base/common/event';
7-
import * as debug from 'vs/workbench/parts/debug/common/debug';
8-
9-
export class ViewModel implements debug.IViewModel {
10-
11-
private _focusedStackFrame: debug.IStackFrame;
12-
private _focusedProcess: debug.IProcess;
13-
private _focusedThread: debug.IThread;
14-
private selectedExpression: debug.IExpression;
15-
private selectedFunctionBreakpoint: debug.IFunctionBreakpoint;
16-
private _onDidFocusProcess: Emitter<debug.IProcess | undefined>;
17-
private _onDidFocusStackFrame: Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>;
18-
private _onDidSelectExpression: Emitter<debug.IExpression>;
7+
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IProcess, IThread, IExpression, IFunctionBreakpoint } from 'vs/workbench/parts/debug/common/debug';
8+
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
9+
10+
export class ViewModel implements IViewModel {
11+
12+
private _focusedStackFrame: IStackFrame;
13+
private _focusedProcess: IProcess;
14+
private _focusedThread: IThread;
15+
private selectedExpression: IExpression;
16+
private selectedFunctionBreakpoint: IFunctionBreakpoint;
17+
private _onDidFocusProcess: Emitter<IProcess | undefined>;
18+
private _onDidFocusStackFrame: Emitter<{ stackFrame: IStackFrame, explicit: boolean }>;
19+
private _onDidSelectExpression: Emitter<IExpression>;
1920
private multiProcessView: boolean;
21+
private expressionSelectedContextKey: IContextKey<boolean>;
2022

21-
constructor() {
22-
this._onDidFocusProcess = new Emitter<debug.IProcess | undefined>();
23-
this._onDidFocusStackFrame = new Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>();
24-
this._onDidSelectExpression = new Emitter<debug.IExpression>();
23+
constructor(contextKeyService: IContextKeyService) {
24+
this._onDidFocusProcess = new Emitter<IProcess | undefined>();
25+
this._onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame, explicit: boolean }>();
26+
this._onDidSelectExpression = new Emitter<IExpression>();
2527
this.multiProcessView = false;
28+
this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService);
2629
}
2730

2831
public getId(): string {
2932
return 'root';
3033
}
3134

32-
public get focusedProcess(): debug.IProcess {
35+
public get focusedProcess(): IProcess {
3336
return this._focusedProcess;
3437
}
3538

36-
public get focusedThread(): debug.IThread {
39+
public get focusedThread(): IThread {
3740
return this._focusedStackFrame ? this._focusedStackFrame.thread : (this._focusedProcess ? this._focusedProcess.getAllThreads().pop() : null);
3841
}
3942

40-
public get focusedStackFrame(): debug.IStackFrame {
43+
public get focusedStackFrame(): IStackFrame {
4144
return this._focusedStackFrame;
4245
}
4346

44-
public setFocus(stackFrame: debug.IStackFrame, thread: debug.IThread, process: debug.IProcess, explicit: boolean): void {
47+
public setFocus(stackFrame: IStackFrame, thread: IThread, process: IProcess, explicit: boolean): void {
4548
let shouldEmit = this._focusedProcess !== process || this._focusedThread !== thread || this._focusedStackFrame !== stackFrame;
4649

4750
if (this._focusedProcess !== process) {
@@ -56,32 +59,33 @@ export class ViewModel implements debug.IViewModel {
5659
}
5760
}
5861

59-
public get onDidFocusProcess(): Event<debug.IProcess> {
62+
public get onDidFocusProcess(): Event<IProcess> {
6063
return this._onDidFocusProcess.event;
6164
}
6265

63-
public get onDidFocusStackFrame(): Event<{ stackFrame: debug.IStackFrame, explicit: boolean }> {
66+
public get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame, explicit: boolean }> {
6467
return this._onDidFocusStackFrame.event;
6568
}
6669

67-
public getSelectedExpression(): debug.IExpression {
70+
public getSelectedExpression(): IExpression {
6871
return this.selectedExpression;
6972
}
7073

71-
public setSelectedExpression(expression: debug.IExpression) {
74+
public setSelectedExpression(expression: IExpression) {
7275
this.selectedExpression = expression;
76+
this.expressionSelectedContextKey.set(!!expression);
7377
this._onDidSelectExpression.fire(expression);
7478
}
7579

76-
public get onDidSelectExpression(): Event<debug.IExpression> {
80+
public get onDidSelectExpression(): Event<IExpression> {
7781
return this._onDidSelectExpression.event;
7882
}
7983

80-
public getSelectedFunctionBreakpoint(): debug.IFunctionBreakpoint {
84+
public getSelectedFunctionBreakpoint(): IFunctionBreakpoint {
8185
return this.selectedFunctionBreakpoint;
8286
}
8387

84-
public setSelectedFunctionBreakpoint(functionBreakpoint: debug.IFunctionBreakpoint): void {
88+
public setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint): void {
8589
this.selectedFunctionBreakpoint = functionBreakpoint;
8690
}
8791

src/vs/workbench/parts/debug/electron-browser/baseDebugView.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as dom from 'vs/base/browser/dom';
77
import { IExpression, IDebugService, IEnablement } from 'vs/workbench/parts/debug/common/debug';
8-
import { Expression, FunctionBreakpoint, Variable } from 'vs/workbench/parts/debug/common/debugModel';
8+
import { Expression, Variable } from 'vs/workbench/parts/debug/common/debugModel';
99
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1010
import { IThemeService } from 'vs/platform/theme/common/themeService';
1111
import { ITree, ContextMenuEvent, IActionProvider } from 'vs/base/parts/tree/browser/tree';
@@ -136,7 +136,6 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
136136
});
137137
const styler = attachInputBoxStyler(inputBox, themeService);
138138

139-
tree.setHighlight();
140139
inputBox.value = options.initialValue ? options.initialValue : '';
141140
inputBox.focus();
142141
inputBox.select();
@@ -149,12 +148,10 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
149148
disposed = true;
150149
if (element instanceof Expression && renamed && inputBox.value) {
151150
debugService.renameWatchExpression(element.getId(), inputBox.value);
151+
debugService.getViewModel().setSelectedExpression(undefined);
152152
} else if (element instanceof Expression && !element.name) {
153153
debugService.removeWatchExpressions(element.getId());
154-
} else if (element instanceof FunctionBreakpoint && inputBox.value) {
155-
debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, onUnexpectedError);
156-
} else if (element instanceof FunctionBreakpoint && !element.name) {
157-
debugService.removeFunctionBreakpoints(element.getId()).done(null, onUnexpectedError);
154+
debugService.getViewModel().setSelectedExpression(undefined);
158155
} else if (element instanceof Variable) {
159156
element.errorMessage = null;
160157
if (renamed && element.value !== inputBox.value) {
@@ -168,7 +165,6 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
168165
}
169166
}
170167

171-
tree.clearHighlight();
172168
tree.DOMFocus();
173169
tree.setFocus(element);
174170

src/vs/workbench/parts/debug/electron-browser/debugCommands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe
1313
import { IListService } from 'vs/platform/list/browser/listService';
1414
import { IMessageService } from 'vs/platform/message/common/message';
1515
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
16-
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL } from 'vs/workbench/parts/debug/common/debug';
16+
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_EXPRESSION_SELECTED } from 'vs/workbench/parts/debug/common/debug';
1717
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel';
1818
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
1919
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -91,7 +91,7 @@ export function registerCommands(): void {
9191
KeybindingsRegistry.registerCommandAndKeybindingRule({
9292
id: 'debug.removeWatchExpression',
9393
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
94-
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
94+
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),
9595
primary: KeyCode.Delete,
9696
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
9797
handler: (accessor) => {

src/vs/workbench/parts/debug/electron-browser/debugService.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class DebugService implements debug.IDebugService {
122122
this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
123123
this.loadExceptionBreakpoints(), this.loadWatchExpressions());
124124
this.toDispose.push(this.model);
125-
this.viewModel = new ViewModel();
125+
this.viewModel = new ViewModel(contextKeyService);
126126
this.firstSessionStart = true;
127127

128128
this.registerListeners();
@@ -642,7 +642,8 @@ export class DebugService implements debug.IDebugService {
642642
}
643643

644644
public addWatchExpression(name: string): void {
645-
return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
645+
const we = this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
646+
this.viewModel.setSelectedExpression(we);
646647
}
647648

648649
public renameWatchExpression(id: string, newName: string): void {

src/vs/workbench/parts/debug/electron-browser/variablesView.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
77
import { RunOnceScheduler, sequence } from 'vs/base/common/async';
88
import * as dom from 'vs/base/browser/dom';
99
import * as errors from 'vs/base/common/errors';
10-
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
10+
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
1111
import { CollapseAction } from 'vs/workbench/browser/viewlet';
1212
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
1313
import { IDebugService, State, CONTEXT_VARIABLES_FOCUSED, IExpression } from 'vs/workbench/parts/debug/common/debug';
@@ -17,7 +17,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
1717
import { MenuId } from 'vs/platform/actions/common/actions';
1818
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1919
import { IThemeService } from 'vs/platform/theme/common/themeService';
20-
import { once } from 'vs/base/common/event';
2120
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
2221
import { TPromise } from 'vs/base/common/winjs.base';
2322
import { IAction, IActionItem } from 'vs/base/common/actions';
@@ -60,8 +59,6 @@ export class VariablesView extends TreeViewsViewletPanel {
6059
this.expandedElements = expanded;
6160
}
6261

63-
// Always clear tree highlight to avoid ending up in a broken state #12203
64-
this.tree.clearHighlight();
6562
this.needsRefresh = false;
6663
this.tree.refresh().then(() => {
6764
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
@@ -123,18 +120,9 @@ export class VariablesView extends TreeViewsViewletPanel {
123120
}));
124121

125122
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
126-
if (!expression || !(expression instanceof Variable)) {
127-
return;
123+
if (expression instanceof Variable) {
124+
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
128125
}
129-
130-
this.tree.refresh(expression, false).then(() => {
131-
this.tree.setHighlight(expression);
132-
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
133-
if (!e.highlight) {
134-
this.debugService.getViewModel().setSelectedExpression(null);
135-
}
136-
});
137-
}).done(null, errors.onUnexpectedError);
138126
}));
139127
}
140128

src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom';
99
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1010
import { TPromise } from 'vs/base/common/winjs.base';
1111
import * as errors from 'vs/base/common/errors';
12-
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
12+
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
1313
import { CollapseAction } from 'vs/workbench/browser/viewlet';
1414
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
1515
import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
@@ -20,7 +20,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
2020
import { MenuId } from 'vs/platform/actions/common/actions';
2121
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
2222
import { IThemeService } from 'vs/platform/theme/common/themeService';
23-
import { once } from 'vs/base/common/event';
2423
import { IAction, IActionItem } from 'vs/base/common/actions';
2524
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
2625
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -104,18 +103,9 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
104103
}));
105104

106105
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
107-
if (!expression || !(expression instanceof Expression)) {
108-
return;
106+
if (expression instanceof Expression) {
107+
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
109108
}
110-
111-
this.tree.refresh(expression, false).then(() => {
112-
this.tree.setHighlight(expression);
113-
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
114-
if (!e.highlight) {
115-
this.debugService.getViewModel().setSelectedExpression(null);
116-
}
117-
});
118-
}).done(null, errors.onUnexpectedError);
119109
}));
120110
}
121111

@@ -298,7 +288,7 @@ class WatchExpressionsRenderer implements IRenderer {
298288

299289
private renderWatchExpression(tree: ITree, watchExpression: IExpression, data: IWatchExpressionTemplateData): void {
300290
let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
301-
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
291+
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId())) {
302292
renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, watchExpression, data.expression, {
303293
initialValue: watchExpression.name,
304294
placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),

src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import * as assert from 'assert';
77
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
88
import { StackFrame, Expression, Thread, Process } from 'vs/workbench/parts/debug/common/debugModel';
99
import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug';
10+
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
1011

1112
suite('Debug - View Model', () => {
1213
let model: ViewModel;
1314

1415
setup(() => {
15-
model = new ViewModel();
16+
model = new ViewModel(new MockContextKeyService());
1617
});
1718

1819
teardown(() => {

0 commit comments

Comments
 (0)