debugViewlet.ts 24.3 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/debugViewlet';
import nls = require('vs/nls');
import dom = require('vs/base/browser/dom');
import builder = require('vs/base/browser/builder');
I
isidor 已提交
10
import { TPromise } from 'vs/base/common/winjs.base';
E
Erich Gamma 已提交
11 12 13 14 15 16
import errors = require('vs/base/common/errors');
import lifecycle = require('vs/base/common/lifecycle');
import events = require('vs/base/common/events');
import actions = require('vs/base/common/actions');
import actionbar = require('vs/base/browser/ui/actionbar/actionbar');
import actionbarregistry = require('vs/workbench/browser/actionBarRegistry');
J
Joao Moreno 已提交
17
import tree = require('vs/base/parts/tree/browser/tree');
E
Erich Gamma 已提交
18 19 20 21 22 23 24
import treeimpl = require('vs/base/parts/tree/browser/treeImpl');
import splitview = require('vs/base/browser/ui/splitview/splitview');
import memento = require('vs/workbench/common/memento');
import viewlet = require('vs/workbench/browser/viewlet');
import debug = require('vs/workbench/parts/debug/common/debug');
import model = require('vs/workbench/parts/debug/common/debugModel');
import viewer = require('vs/workbench/parts/debug/browser/debugViewer');
I
isidor 已提交
25
import debugactions = require('vs/workbench/parts/debug/electron-browser/debugActions');
E
Erich Gamma 已提交
26 27 28 29 30 31 32 33 34 35 36 37
import dbgactionitems = require('vs/workbench/parts/debug/browser/debugActionItems');
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IMessageService } from 'vs/platform/message/common/message';
import { IStorageService } from 'vs/platform/storage/common/storage';

import IDebugService = debug.IDebugService;

function renderViewTree(container: HTMLElement): HTMLElement {
I
isidor 已提交
38
	const treeContainer = document.createElement('div');
E
Erich Gamma 已提交
39 40 41 42 43
	dom.addClass(treeContainer, 'debug-view-content');
	container.appendChild(treeContainer);
	return treeContainer;
}

B
Benjamin Pasero 已提交
44 45 46 47 48 49
const debugTreeOptions = (ariaLabel: string) => {
	return <tree.ITreeOptions> {
		indentPixels: 8,
		twistiePixels: 20,
		ariaLabel
	};
E
Erich Gamma 已提交
50 51
};

I
isidor 已提交
52
const $ = builder.$;
E
Erich Gamma 已提交
53 54 55 56 57 58 59 60

class VariablesView extends viewlet.CollapsibleViewletView {

	private static MEMENTO = 'variablesview.memento';

	constructor(actionRunner: actions.IActionRunner, private settings: any,
		@IMessageService messageService: IMessageService,
		@IContextMenuService contextMenuService: IContextMenuService,
61
		@ITelemetryService private telemetryService: ITelemetryService,
E
Erich Gamma 已提交
62
		@IDebugService private debugService: IDebugService,
P
Pierson Lee 已提交
63
		@IInstantiationService private instantiationService: IInstantiationService
E
Erich Gamma 已提交
64
	) {
65
		super(actionRunner, !!settings[VariablesView.MEMENTO], nls.localize('variablesSection', "Variables Section"), messageService, contextMenuService);
E
Erich Gamma 已提交
66 67 68 69
	}

	public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
I
isidor 已提交
70
		const titleDiv = $('div.title').appendTo(container);
E
Erich Gamma 已提交
71 72 73 74
		$('span').text(nls.localize('variables', "Variables")).appendTo(titleDiv);
	}

	public renderBody(container: HTMLElement): void {
75
		dom.addClass(container, 'debug-variables');
E
Erich Gamma 已提交
76 77 78 79 80
		this.treeContainer = renderViewTree(container);

		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.VariablesDataSource(this.debugService),
			renderer: this.instantiationService.createInstance(viewer.VariablesRenderer),
81
			accessibilityProvider: new viewer.VariablesAccessibilityProvider(),
E
Erich Gamma 已提交
82
			controller: new viewer.BaseDebugController(this.debugService, this.contextMenuService, new viewer.VariablesActionProvider(this.instantiationService))
83
		}, debugTreeOptions(nls.localize('variablesAriaTreeLabel', "Debug Variables")));
E
Erich Gamma 已提交
84

I
isidor 已提交
85
		const viewModel = this.debugService.getViewModel();
E
Erich Gamma 已提交
86 87 88

		this.tree.setInput(viewModel);

I
isidor 已提交
89
		const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
E
Erich Gamma 已提交
90 91 92 93 94 95
		this.toolBar.setActions(actionbarregistry.prepareActions([collapseAction]))();

		this.toDispose.push(viewModel.addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => this.onFocusedStackFrameUpdated()));
		this.toDispose.push(this.debugService.addListener2(debug.ServiceEvents.STATE_CHANGED, () => {
			collapseAction.enabled = this.debugService.getState() === debug.State.Running || this.debugService.getState() === debug.State.Stopped;
		}));
96

P
Pierson Lee 已提交
97
		this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
I
isidor 已提交
98
			const isMouseClick = (e.payload && e.payload.origin === 'mouse');
P
Pierson Lee 已提交
99 100 101
			const isVariableType = (e.focus instanceof model.Variable);

			if(isMouseClick && isVariableType) {
I
isidor 已提交
102
				this.telemetryService.publicLog('debug/variables/selected');
P
Pierson Lee 已提交
103 104
			}
		}));
E
Erich Gamma 已提交
105 106 107 108
	}

	private onFocusedStackFrameUpdated(): void {
		this.tree.refresh().then(() => {
I
isidor 已提交
109
			const stackFrame = this.debugService.getViewModel().getFocusedStackFrame();
E
Erich Gamma 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
			if (stackFrame) {
				return stackFrame.getScopes(this.debugService).then(scopes => {
					if (scopes.length > 0) {
						return this.tree.expand(scopes[0]);
					}
				});
			}
		}).done(null, errors.onUnexpectedError);
	}

	public shutdown(): void {
		this.settings[VariablesView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}
}

class WatchExpressionsView extends viewlet.CollapsibleViewletView {

	private static MEMENTO = 'watchexpressionsview.memento';

	constructor(actionRunner: actions.IActionRunner, private settings: any,
		@IMessageService messageService: IMessageService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IDebugService private debugService: IDebugService,
		@IInstantiationService private instantiationService: IInstantiationService
	) {
136
		super(actionRunner, !!settings[WatchExpressionsView.MEMENTO], nls.localize('expressionsSection', "Expressions Section"), messageService, contextMenuService);
E
Erich Gamma 已提交
137
		this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.WATCH_EXPRESSIONS_UPDATED, (we) => {
I
isidor 已提交
138
			// only expand when a new watch expression is added.
E
Erich Gamma 已提交
139 140 141 142 143 144 145 146
			if (we instanceof model.Expression) {
				this.expand();
			}
		}));
	}

	public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
I
isidor 已提交
147
		const titleDiv = $('div.title').appendTo(container);
E
Erich Gamma 已提交
148 149 150 151
		$('span').text(nls.localize('watch', "Watch")).appendTo(titleDiv);
	}

	public renderBody(container: HTMLElement): void {
152
		dom.addClass(container, 'debug-watch');
E
Erich Gamma 已提交
153 154
		this.treeContainer = renderViewTree(container);

I
isidor 已提交
155
		const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService);
E
Erich Gamma 已提交
156 157 158
		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.WatchExpressionsDataSource(this.debugService),
			renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner),
159
			accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(),
E
Erich Gamma 已提交
160
			controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider)
161
		}, debugTreeOptions(nls.localize('watchAriaTreeLabel', "Debug Watch Expressions")));
E
Erich Gamma 已提交
162 163 164

		this.tree.setInput(this.debugService.getModel());

I
isidor 已提交
165
		const addWatchExpressionAction = this.instantiationService.createInstance(debugactions.AddWatchExpressionAction, debugactions.AddWatchExpressionAction.ID, debugactions.AddWatchExpressionAction.LABEL);
I
isidor 已提交
166
		const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
I
isidor 已提交
167
		const removeAllWatchExpressionsAction = this.instantiationService.createInstance(debugactions.RemoveAllWatchExpressionsAction, debugactions.RemoveAllWatchExpressionsAction.ID, debugactions.RemoveAllWatchExpressionsAction.LABEL);
E
Erich Gamma 已提交
168 169 170 171 172 173 174 175 176 177
		this.toolBar.setActions(actionbarregistry.prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();

		this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.WATCH_EXPRESSIONS_UPDATED, (we: model.Expression) => this.onWatchExpressionsUpdated(we)));
		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.SELECTED_EXPRESSION_UPDATED, (expression: debug.IExpression) => {
			if (!expression || !(expression instanceof model.Expression)) {
				return;
			}

			this.tree.refresh(expression, false).then(() => {
				this.tree.setHighlight(expression);
I
isidor 已提交
178
				this.tree.addOneTimeListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => {
E
Erich Gamma 已提交
179 180 181 182 183 184 185 186 187 188
					if (!e.highlight) {
						this.debugService.getViewModel().setSelectedExpression(null);
					}
				});
			}).done(null, errors.onUnexpectedError);
		}));
	}

	private onWatchExpressionsUpdated(we: model.Expression): void {
		this.tree.refresh().done(() => {
A
Alex Dima 已提交
189
			return we instanceof model.Expression ? this.tree.reveal(we): TPromise.as(true);
E
Erich Gamma 已提交
190 191 192 193 194 195 196 197 198 199 200 201
		}, errors.onUnexpectedError);
	}

	public shutdown(): void {
		this.settings[WatchExpressionsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}
}

class CallStackView extends viewlet.CollapsibleViewletView {

	private static MEMENTO = 'callstackview.memento';
202 203
	private pauseMessage: builder.Builder;
	private pauseMessageLabel: builder.Builder;
E
Erich Gamma 已提交
204 205 206 207

	constructor(actionRunner: actions.IActionRunner, private settings: any,
		@IMessageService messageService: IMessageService,
		@IContextMenuService contextMenuService: IContextMenuService,
208
		@ITelemetryService private telemetryService: ITelemetryService,
E
Erich Gamma 已提交
209
		@IDebugService private debugService: IDebugService,
P
Pierson Lee 已提交
210
		@IInstantiationService private instantiationService: IInstantiationService
E
Erich Gamma 已提交
211
	) {
212
		super(actionRunner, !!settings[CallStackView.MEMENTO], nls.localize('callstackSection', "Call Stack Section"), messageService, contextMenuService);
E
Erich Gamma 已提交
213 214 215 216
	}

	public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
217 218 219 220 221
		const title = $('div.debug-call-stack-title').appendTo(container);
		$('span.title').text(nls.localize('callStack', "Call Stack")).appendTo(title);
		this.pauseMessage = $('span.pause-message').appendTo(title);
		this.pauseMessage.hide();
		this.pauseMessageLabel = $('span.label').appendTo(this.pauseMessage);
E
Erich Gamma 已提交
222 223 224
	}

	public renderBody(container: HTMLElement): void {
225
		dom.addClass(container, 'debug-call-stack');
E
Erich Gamma 已提交
226 227 228
		this.treeContainer = renderViewTree(container);

		this.tree = new treeimpl.Tree(this.treeContainer, {
229
			dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource),
230 231
			renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
			accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider)
232
		}, debugTreeOptions(nls.localize('callStackAriaLabel', "Debug Call Stack")));
E
Erich Gamma 已提交
233

I
isidor 已提交
234
		const debugModel = this.debugService.getModel();
E
Erich Gamma 已提交
235 236 237 238 239 240 241

		this.tree.setInput(debugModel);

		this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
			if (!e.selection.length) {
				return;
			}
I
isidor 已提交
242
			const element = e.selection[0];
E
Erich Gamma 已提交
243 244 245 246
			if (!(element instanceof model.StackFrame)) {
				return;
			}

I
isidor 已提交
247
			const stackFrame = <debug.IStackFrame> element;
E
Erich Gamma 已提交
248 249
			this.debugService.setFocusedStackFrameAndEvaluate(stackFrame);

I
isidor 已提交
250 251
			const isMouse = (e.payload.origin === 'mouse');
			let preserveFocus = isMouse;
E
Erich Gamma 已提交
252

I
isidor 已提交
253
			const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
E
Erich Gamma 已提交
254 255 256 257 258
			if (originalEvent && isMouse && originalEvent.detail === 2) {
				preserveFocus = false;
				originalEvent.preventDefault();  // focus moves to editor, we need to prevent default
			}

I
isidor 已提交
259
			const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
E
Erich Gamma 已提交
260 261
			this.debugService.openOrRevealEditor(stackFrame.source, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
		}));
I
isidor 已提交
262

P
Pierson Lee 已提交
263
		this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
I
isidor 已提交
264
			const isMouseClick = (e.payload && e.payload.origin === 'mouse');
P
Pierson Lee 已提交
265 266 267
			const isStackFrameType = (e.focus instanceof model.StackFrame);

			if (isMouseClick && isStackFrameType) {
I
isidor 已提交
268
				this.telemetryService.publicLog('debug/callStack/selected');
P
Pierson Lee 已提交
269 270
			}
		}));
E
Erich Gamma 已提交
271 272 273 274

		this.toDispose.push(debugModel.addListener2(debug.ModelEvents.CALLSTACK_UPDATED, () => {
			this.tree.refresh().done(null, errors.onUnexpectedError);
		}));
I
isidor 已提交
275

276 277
		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => {
			const focussedThread = this.debugService.getModel().getThreads()[this.debugService.getViewModel().getFocusedThreadId()];
278
			if (focussedThread && focussedThread.stoppedDetails && focussedThread.stoppedDetails.reason && focussedThread.stoppedDetails.reason !== 'step') {
I
isidor 已提交
279 280 281 282 283
				this.pauseMessageLabel.text(nls.localize('debugStopped', "Paused on {0}", focussedThread.stoppedDetails.reason));
				if (focussedThread.stoppedDetails.text) {
					this.pauseMessageLabel.title(focussedThread.stoppedDetails.text);
				}
				focussedThread.stoppedDetails.reason === 'exception' ? this.pauseMessageLabel.addClass('exception') : this.pauseMessageLabel.removeClass('exception');
284
				this.pauseMessage.show();
285
			} else {
286
				this.pauseMessage.hide();
E
Erich Gamma 已提交
287 288 289 290
			}
		}));

		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED,() => {
I
isidor 已提交
291
			const focused = this.debugService.getViewModel().getFocusedStackFrame();
E
Erich Gamma 已提交
292
			if (focused) {
I
isidor 已提交
293 294
				const threads = this.debugService.getModel().getThreads();
				for (let ref in threads) {
295 296 297 298 299
					// Only query for threads whose callstacks are already available
					// so that we don't perform unnecessary queries to the
					// debug adapter. If it's a thread we need to expand, its
					// callstack would have already been populated already
					if (threads[ref].getCachedCallStack() && threads[ref].getCachedCallStack().some(sf => sf === focused)) {
E
Erich Gamma 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
						this.tree.expand(threads[ref]);
					}
				}
				this.tree.setFocus(focused);
			}
		}));
	}

	public shutdown(): void {
		this.settings[CallStackView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}
}

class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView {

	private static MAX_VISIBLE_FILES = 9;
	private static MEMENTO = 'breakopintsview.memento';

	constructor(actionRunner: actions.IActionRunner, private settings: any,
		@IMessageService messageService: IMessageService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IDebugService private debugService: IDebugService,
		@IInstantiationService private instantiationService: IInstantiationService
	) {
		super(actionRunner, BreakpointsView.getExpandedBodySize(
326
			debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length),
327
			!!settings[BreakpointsView.MEMENTO], nls.localize('breakpointsSection', "Breakpoints Section"), messageService, contextMenuService);
E
Erich Gamma 已提交
328 329 330 331 332 333

		this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.BREAKPOINTS_UPDATED,() => this.onBreakpointsChange()));
	}

	public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
I
isidor 已提交
334
		const titleDiv = $('div.title').appendTo(container);
E
Erich Gamma 已提交
335 336 337 338
		$('span').text(nls.localize('breakpoints', "Breakpoints")).appendTo(titleDiv);
	}

	public renderBody(container: HTMLElement): void {
339
		dom.addClass(container, 'debug-breakpoints');
E
Erich Gamma 已提交
340
		this.treeContainer = renderViewTree(container);
I
isidor 已提交
341
		const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService);
E
Erich Gamma 已提交
342 343 344 345

		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.BreakpointsDataSource(),
			renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner),
346
			accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider),
E
Erich Gamma 已提交
347 348 349
			controller: new viewer.BreakpointsController(this.debugService, this.contextMenuService, actionProvider),
			sorter: {
				compare(tree: tree.ITree, element: any, otherElement: any): number {
I
isidor 已提交
350 351
					const first = <debug.IBreakpoint> element;
					const second = <debug.IBreakpoint> otherElement;
E
Erich Gamma 已提交
352 353 354
					if (first instanceof model.ExceptionBreakpoint) {
						return -1;
					}
355
					if (second instanceof model.ExceptionBreakpoint) {
E
Erich Gamma 已提交
356 357
						return 1;
					}
358 359 360
					if (first instanceof model.FunctionBreakpoint) {
						return -1;
					}
361 362 363
					if(second instanceof model.FunctionBreakpoint) {
						return 1;
					}
E
Erich Gamma 已提交
364 365 366 367 368 369 370 371

					if (first.source.uri.toString() !== second.source.uri.toString()) {
						return first.source.uri.toString().localeCompare(second.source.uri.toString());
					}

					return first.desiredLineNumber - second.desiredLineNumber;
				}
			}
372
		}, debugTreeOptions(nls.localize('breakpointsAriaTreeLabel', "Debug Breakpoints")));
E
Erich Gamma 已提交
373

I
isidor 已提交
374
		const debugModel = this.debugService.getModel();
E
Erich Gamma 已提交
375 376 377 378 379 380 381

		this.tree.setInput(debugModel);

		this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
			if (!e.selection.length) {
				return;
			}
I
isidor 已提交
382
			const element = e.selection[0];
E
Erich Gamma 已提交
383 384 385 386
			if (!(element instanceof model.Breakpoint)) {
				return;
			}

I
isidor 已提交
387
			const breakpoint = <debug.IBreakpoint> element;
E
Erich Gamma 已提交
388
			if (!breakpoint.source.inMemory) {
I
isidor 已提交
389 390
				const isMouse = (e.payload.origin === 'mouse');
				let preserveFocus = isMouse;
E
Erich Gamma 已提交
391

I
isidor 已提交
392
				const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
E
Erich Gamma 已提交
393 394 395 396 397
				if (originalEvent && isMouse && originalEvent.detail === 2) {
					preserveFocus = false;
					originalEvent.preventDefault();  // focus moves to editor, we need to prevent default
				}

I
isidor 已提交
398
				const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
E
Erich Gamma 已提交
399 400 401
				this.debugService.openOrRevealEditor(breakpoint.source, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
			}
		}));
I
isidor 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.SELECTED_FUNCTION_BREAKPOINT_UPDATED, (fbp: debug.IFunctionBreakpoint) => {
			if (!fbp || !(fbp instanceof model.FunctionBreakpoint)) {
				return;
			}

			this.tree.refresh(fbp, false).then(() => {
				this.tree.setHighlight(fbp);
				this.tree.addOneTimeListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => {
					if (!e.highlight) {
						this.debugService.getViewModel().setSelectedFunctionBreakpoint(null);
					}
				});
			}).done(null, errors.onUnexpectedError);
		}));
E
Erich Gamma 已提交
417 418 419 420
	}

	public getActions(): actions.IAction[] {
		return [
I
isidor 已提交
421
			this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL),
I
isidor 已提交
422 423
			this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL),
			this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL)
E
Erich Gamma 已提交
424 425 426 427
		];
	}

	private onBreakpointsChange(): void {
428 429 430
		const model = this.debugService.getModel();
		this.expandedBodySize = BreakpointsView.getExpandedBodySize(
			model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length);
E
Erich Gamma 已提交
431 432 433 434 435 436 437

		if (this.tree) {
			this.tree.refresh();
		}
	}

	private static getExpandedBodySize(length: number): number {
I
isidor 已提交
438
		return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
E
Erich Gamma 已提交
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
	}

	public shutdown(): void {
		this.settings[BreakpointsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}
}

export class DebugViewlet extends viewlet.Viewlet {

	private toDispose: lifecycle.IDisposable[];
	private actions: actions.IAction[];
	private progressRunner: IProgressRunner;
	private viewletSettings: any;

	private $el: builder.Builder;
	private splitView: splitview.SplitView;
	private views: viewlet.IViewletView[];

458 459
	private lastFocusedView: viewlet.CollapsibleViewletView | viewlet.AdaptiveCollapsibleViewletView;

E
Erich Gamma 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IProgressService private progressService: IProgressService,
		@IDebugService private debugService: IDebugService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IStorageService storageService: IStorageService
	) {
		super(debug.VIEWLET_ID, telemetryService);

		this.progressRunner = null;
		this.viewletSettings = this.getMemento(storageService, memento.Scope.WORKSPACE);
		this.views = [];
		this.toDispose = [];
		this.toDispose.push(this.debugService.addListener2(debug.ServiceEvents.STATE_CHANGED, () => {
			this.onDebugServiceStateChange();
		}));
	}

I
isidor 已提交
479
	// viewlet
E
Erich Gamma 已提交
480 481 482 483 484 485

	public create(parent: builder.Builder): TPromise<void> {
		super.create(parent);
		this.$el = parent.div().addClass('debug-viewlet');

		if (this.contextService.getWorkspace()) {
I
isidor 已提交
486
			const actionRunner = this.getActionRunner();
E
Erich Gamma 已提交
487 488 489 490 491 492 493 494
			this.views.push(this.instantiationService.createInstance(VariablesView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(WatchExpressionsView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(CallStackView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(BreakpointsView, actionRunner, this.viewletSettings));

			this.splitView = new splitview.SplitView(this.$el.getHTMLElement());
			this.toDispose.push(this.splitView);
			this.views.forEach(v => this.splitView.addView(<any> v));
495 496 497 498 499

			// Track focus
			this.toDispose.push(this.splitView.onFocus((view: viewlet.CollapsibleViewletView | viewlet.AdaptiveCollapsibleViewletView) => {
				this.lastFocusedView = view;
			}));
E
Erich Gamma 已提交
500 501 502 503 504 505 506 507 508
		} else {
			this.$el.append($([
				'<div class="noworkspace-view">',
				'<p>', nls.localize('noWorkspace', "There is no currently opened folder."), '</p>',
				'<p>', nls.localize('pleaseRestartToDebug', "Open a folder in order to start debugging."), '</p>',
				'</div>'
			].join('')));
		}

A
Alex Dima 已提交
509
		return TPromise.as(null);
E
Erich Gamma 已提交
510 511
	}

I
isidor 已提交
512
	public setVisible(visible: boolean): TPromise<any> {
513
		return super.setVisible(visible).then(() => {
I
isidor 已提交
514
			return TPromise.join(this.views.map((view) => view.setVisible(visible)));
515 516 517
		});
	}

E
Erich Gamma 已提交
518 519 520 521 522 523
	public layout(dimension: builder.Dimension): void {
		if (this.splitView) {
			this.splitView.layout(dimension.height);
		}
	}

I
isidor 已提交
524 525
	public focus(): void {
		super.focus();
526

527 528 529 530 531
		if (this.lastFocusedView && this.lastFocusedView.isExpanded()) {
			this.lastFocusedView.focusBody();
			return;
		}

I
isidor 已提交
532
		if (this.views.length > 0) {
533
			(<VariablesView>this.views[0]).focusBody();
I
isidor 已提交
534 535 536
		}
	}

E
Erich Gamma 已提交
537 538 539 540 541 542 543
	public getActions(): actions.IAction[] {
		if (this.debugService.getState() === debug.State.Disabled) {
			return [];
		}

		if (!this.actions) {
			this.actions = [
I
isidor 已提交
544 545 546 547
				this.instantiationService.createInstance(debugactions.StartDebugAction, debugactions.StartDebugAction.ID, debugactions.StartDebugAction.LABEL),
				this.instantiationService.createInstance(debugactions.SelectConfigAction, debugactions.SelectConfigAction.ID, debugactions.SelectConfigAction.LABEL),
				this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
				this.instantiationService.createInstance(debugactions.ToggleReplAction, debugactions.ToggleReplAction.ID, debugactions.ToggleReplAction.LABEL)
E
Erich Gamma 已提交
548 549 550 551 552 553 554 555 556 557 558
			];

			this.actions.forEach(a => {
				this.toDispose.push(a);
			});
		}

		return this.actions;
	}

	public getActionItem(action: actions.IAction): actionbar.IActionItem {
I
isidor 已提交
559
		if (action.id === debugactions.SelectConfigAction.ID) {
E
Erich Gamma 已提交
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
			return this.instantiationService.createInstance(dbgactionitems.SelectConfigActionItem, action);
		}

		return null;
	}

	private onDebugServiceStateChange(): void {
		if (this.progressRunner) {
			this.progressRunner.done();
		}

		if (this.debugService.getState() === debug.State.Initializing) {
			this.progressRunner = this.progressService.show(true);
		} else {
			this.progressRunner = null;
		}
	}

	public dispose(): void {
		this.toDispose = lifecycle.disposeAll(this.toDispose);

		super.dispose();
	}

	public shutdown(): void {
		this.views.forEach(v => v.shutdown());
		super.shutdown();
	}
}