mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-05 20:44:43 +01:00
debug: mechanism to register split views
This commit is contained in:
@@ -5,451 +5,26 @@
|
||||
|
||||
import 'vs/css!./media/debugViewlet';
|
||||
import nls = require('vs/nls');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import builder = require('vs/base/browser/builder');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
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');
|
||||
import tree = require('vs/base/parts/tree/browser/tree');
|
||||
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');
|
||||
import debugactions = require('vs/workbench/parts/debug/electron-browser/debugActions');
|
||||
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 {
|
||||
const treeContainer = document.createElement('div');
|
||||
dom.addClass(treeContainer, 'debug-view-content');
|
||||
container.appendChild(treeContainer);
|
||||
return treeContainer;
|
||||
}
|
||||
|
||||
const debugTreeOptions = (ariaLabel: string) => {
|
||||
return <tree.ITreeOptions> {
|
||||
indentPixels: 8,
|
||||
twistiePixels: 20,
|
||||
ariaLabel
|
||||
};
|
||||
};
|
||||
|
||||
const $ = builder.$;
|
||||
|
||||
class VariablesView extends viewlet.CollapsibleViewletView {
|
||||
|
||||
private static MEMENTO = 'variablesview.memento';
|
||||
|
||||
constructor(actionRunner: actions.IActionRunner, private settings: any,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(actionRunner, !!settings[VariablesView.MEMENTO], nls.localize('variablesSection', "Variables Section"), messageService, contextMenuService);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('variables', "Variables")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.VariablesDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(viewer.VariablesRenderer),
|
||||
accessibilityProvider: new viewer.VariablesAccessibilityProvider(),
|
||||
controller: new viewer.BaseDebugController(this.debugService, this.contextMenuService, new viewer.VariablesActionProvider(this.instantiationService))
|
||||
}, debugTreeOptions(nls.localize('variablesAriaTreeLabel', "Debug Variables")));
|
||||
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
|
||||
this.tree.setInput(viewModel);
|
||||
|
||||
const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
|
||||
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;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
|
||||
const isMouseClick = (e.payload && e.payload.origin === 'mouse');
|
||||
const isVariableType = (e.focus instanceof model.Variable);
|
||||
|
||||
if(isMouseClick && isVariableType) {
|
||||
this.telemetryService.publicLog('debug/variables/selected');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onFocusedStackFrameUpdated(): void {
|
||||
this.tree.refresh().then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().getFocusedStackFrame();
|
||||
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
|
||||
) {
|
||||
super(actionRunner, !!settings[WatchExpressionsView.MEMENTO], nls.localize('expressionsSection', "Expressions Section"), messageService, contextMenuService);
|
||||
this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.WATCH_EXPRESSIONS_UPDATED, (we) => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof model.Expression) {
|
||||
this.expand();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('watch', "Watch")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-watch');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService);
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.WatchExpressionsDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner),
|
||||
accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(),
|
||||
controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider)
|
||||
}, debugTreeOptions(nls.localize('watchAriaTreeLabel', "Debug Watch Expressions")));
|
||||
|
||||
this.tree.setInput(this.debugService.getModel());
|
||||
|
||||
const addWatchExpressionAction = this.instantiationService.createInstance(debugactions.AddWatchExpressionAction, debugactions.AddWatchExpressionAction.ID, debugactions.AddWatchExpressionAction.LABEL);
|
||||
const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
|
||||
const removeAllWatchExpressionsAction = this.instantiationService.createInstance(debugactions.RemoveAllWatchExpressionsAction, debugactions.RemoveAllWatchExpressionsAction.ID, debugactions.RemoveAllWatchExpressionsAction.LABEL);
|
||||
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);
|
||||
this.tree.addOneTimeListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
private onWatchExpressionsUpdated(we: model.Expression): void {
|
||||
this.tree.refresh().done(() => {
|
||||
return we instanceof model.Expression ? this.tree.reveal(we): TPromise.as(true);
|
||||
}, 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';
|
||||
private pauseMessage: builder.Builder;
|
||||
private pauseMessageLabel: builder.Builder;
|
||||
|
||||
constructor(actionRunner: actions.IActionRunner, private settings: any,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(actionRunner, !!settings[CallStackView.MEMENTO], nls.localize('callstackSection', "Call Stack Section"), messageService, contextMenuService);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
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);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource),
|
||||
renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
|
||||
accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider)
|
||||
}, debugTreeOptions(nls.localize('callStackAriaLabel', "Debug Call Stack")));
|
||||
|
||||
const debugModel = this.debugService.getModel();
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
|
||||
if (!e.selection.length) {
|
||||
return;
|
||||
}
|
||||
const element = e.selection[0];
|
||||
|
||||
if (element instanceof model.StackFrame) {
|
||||
const stackFrame = <debug.IStackFrame> element;
|
||||
this.debugService.setFocusedStackFrameAndEvaluate(stackFrame);
|
||||
|
||||
const isMouse = (e.payload.origin === 'mouse');
|
||||
let preserveFocus = isMouse;
|
||||
|
||||
const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
|
||||
if (originalEvent && isMouse && originalEvent.detail === 2) {
|
||||
preserveFocus = false;
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
|
||||
this.debugService.openOrRevealEditor(stackFrame.source, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
// user clicked on 'Load More Stack Frames', get those stack frames and refresh the tree.
|
||||
if (typeof element === 'number') {
|
||||
const thread = this.debugService.getModel().getThreads()[element];
|
||||
if (thread) {
|
||||
thread.getCallStack(this.debugService, true)
|
||||
.then(() => this.tree.refresh())
|
||||
.then(() => {
|
||||
this.tree.clearFocus();
|
||||
this.tree.clearSelection();
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
|
||||
const isMouseClick = (e.payload && e.payload.origin === 'mouse');
|
||||
const isStackFrameType = (e.focus instanceof model.StackFrame);
|
||||
|
||||
if (isMouseClick && isStackFrameType) {
|
||||
this.telemetryService.publicLog('debug/callStack/selected');
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(debugModel.addListener2(debug.ModelEvents.CALLSTACK_UPDATED, () => {
|
||||
this.tree.refresh().done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => {
|
||||
const focussedThread = this.debugService.getModel().getThreads()[this.debugService.getViewModel().getFocusedThreadId()];
|
||||
if (!focussedThread) {
|
||||
this.pauseMessage.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.expand(focussedThread);
|
||||
this.tree.setFocus(this.debugService.getViewModel().getFocusedStackFrame());
|
||||
if (focussedThread.stoppedDetails && focussedThread.stoppedDetails.reason) {
|
||||
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');
|
||||
this.pauseMessage.show();
|
||||
} else {
|
||||
this.pauseMessage.hide();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
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(
|
||||
debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length),
|
||||
!!settings[BreakpointsView.MEMENTO], nls.localize('breakpointsSection', "Breakpoints Section"), messageService, contextMenuService);
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.BREAKPOINTS_UPDATED,() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('breakpoints', "Breakpoints")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-breakpoints');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.BreakpointsDataSource(),
|
||||
renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner),
|
||||
accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider),
|
||||
controller: new viewer.BreakpointsController(this.debugService, this.contextMenuService, actionProvider),
|
||||
sorter: {
|
||||
compare(tree: tree.ITree, element: any, otherElement: any): number {
|
||||
const first = <debug.IBreakpoint> element;
|
||||
const second = <debug.IBreakpoint> otherElement;
|
||||
if (first instanceof model.ExceptionBreakpoint) {
|
||||
return -1;
|
||||
}
|
||||
if (second instanceof model.ExceptionBreakpoint) {
|
||||
return 1;
|
||||
}
|
||||
if (first instanceof model.FunctionBreakpoint) {
|
||||
return -1;
|
||||
}
|
||||
if(second instanceof model.FunctionBreakpoint) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (first.source.uri.toString() !== second.source.uri.toString()) {
|
||||
return first.source.uri.toString().localeCompare(second.source.uri.toString());
|
||||
}
|
||||
|
||||
return first.desiredLineNumber - second.desiredLineNumber;
|
||||
}
|
||||
}
|
||||
}, debugTreeOptions(nls.localize('breakpointsAriaTreeLabel', "Debug Breakpoints")));
|
||||
|
||||
const debugModel = this.debugService.getModel();
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
|
||||
if (!e.selection.length) {
|
||||
return;
|
||||
}
|
||||
const element = e.selection[0];
|
||||
if (!(element instanceof model.Breakpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const breakpoint = <debug.IBreakpoint> element;
|
||||
if (!breakpoint.source.inMemory) {
|
||||
const isMouse = (e.payload.origin === 'mouse');
|
||||
let preserveFocus = isMouse;
|
||||
|
||||
const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
|
||||
if (originalEvent && isMouse && originalEvent.detail === 2) {
|
||||
preserveFocus = false;
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
|
||||
this.debugService.openOrRevealEditor(breakpoint.source, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
|
||||
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);
|
||||
}));
|
||||
}
|
||||
|
||||
public getActions(): actions.IAction[] {
|
||||
return [
|
||||
this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL),
|
||||
this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL),
|
||||
this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL)
|
||||
];
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const model = this.debugService.getModel();
|
||||
this.expandedBodySize = BreakpointsView.getExpandedBodySize(
|
||||
model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length);
|
||||
|
||||
if (this.tree) {
|
||||
this.tree.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private static getExpandedBodySize(length: number): number {
|
||||
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[BreakpointsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class DebugViewlet extends viewlet.Viewlet {
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
@@ -459,7 +34,7 @@ export class DebugViewlet extends viewlet.Viewlet {
|
||||
|
||||
private $el: builder.Builder;
|
||||
private splitView: splitview.SplitView;
|
||||
private views: viewlet.IViewletView[];
|
||||
private views: (viewlet.CollapsibleViewletView | viewlet.AdaptiveCollapsibleViewletView)[];
|
||||
|
||||
private lastFocusedView: viewlet.CollapsibleViewletView | viewlet.AdaptiveCollapsibleViewletView;
|
||||
|
||||
@@ -475,7 +50,6 @@ export class DebugViewlet extends viewlet.Viewlet {
|
||||
|
||||
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();
|
||||
@@ -490,14 +64,12 @@ export class DebugViewlet extends viewlet.Viewlet {
|
||||
|
||||
if (this.contextService.getWorkspace()) {
|
||||
const actionRunner = this.getActionRunner();
|
||||
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));
|
||||
const viewDescriptors = debug.DebugViewRegistry.getDebugViews();
|
||||
this.views = viewDescriptors.map(dsc => this.instantiationService.createInstance(dsc, 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));
|
||||
this.views.forEach(v => this.splitView.addView(v));
|
||||
|
||||
// Track focus
|
||||
this.toDispose.push(this.splitView.onFocus((view: viewlet.CollapsibleViewletView | viewlet.AdaptiveCollapsibleViewletView) => {
|
||||
@@ -536,7 +108,7 @@ export class DebugViewlet extends viewlet.Viewlet {
|
||||
}
|
||||
|
||||
if (this.views.length > 0) {
|
||||
(<VariablesView>this.views[0]).focusBody();
|
||||
this.views[0].focusBody();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import builder = require('vs/base/browser/builder');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import events = require('vs/base/common/events');
|
||||
import actions = require('vs/base/common/actions');
|
||||
import actionbarregistry = require('vs/workbench/browser/actionBarRegistry');
|
||||
import tree = require('vs/base/parts/tree/browser/tree');
|
||||
import treeimpl = require('vs/base/parts/tree/browser/treeImpl');
|
||||
import splitview = require('vs/base/browser/ui/splitview/splitview');
|
||||
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');
|
||||
import debugactions = require('vs/workbench/parts/debug/electron-browser/debugActions');
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
|
||||
import IDebugService = debug.IDebugService;
|
||||
|
||||
const debugTreeOptions = (ariaLabel: string) => {
|
||||
return <tree.ITreeOptions> {
|
||||
indentPixels: 8,
|
||||
twistiePixels: 20,
|
||||
ariaLabel
|
||||
};
|
||||
};
|
||||
|
||||
function renderViewTree(container: HTMLElement): HTMLElement {
|
||||
const treeContainer = document.createElement('div');
|
||||
dom.addClass(treeContainer, 'debug-view-content');
|
||||
container.appendChild(treeContainer);
|
||||
return treeContainer;
|
||||
}
|
||||
|
||||
const $ = builder.$;
|
||||
|
||||
export class VariablesView extends viewlet.CollapsibleViewletView {
|
||||
|
||||
private static MEMENTO = 'variablesview.memento';
|
||||
|
||||
constructor(actionRunner: actions.IActionRunner, private settings: any,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(actionRunner, !!settings[VariablesView.MEMENTO], nls.localize('variablesSection', "Variables Section"), messageService, contextMenuService);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('variables', "Variables")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.VariablesDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(viewer.VariablesRenderer),
|
||||
accessibilityProvider: new viewer.VariablesAccessibilityProvider(),
|
||||
controller: new viewer.BaseDebugController(this.debugService, this.contextMenuService, new viewer.VariablesActionProvider(this.instantiationService))
|
||||
}, debugTreeOptions(nls.localize('variablesAriaTreeLabel', "Debug Variables")));
|
||||
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
|
||||
this.tree.setInput(viewModel);
|
||||
|
||||
const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
|
||||
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;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
|
||||
const isMouseClick = (e.payload && e.payload.origin === 'mouse');
|
||||
const isVariableType = (e.focus instanceof model.Variable);
|
||||
|
||||
if(isMouseClick && isVariableType) {
|
||||
this.telemetryService.publicLog('debug/variables/selected');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onFocusedStackFrameUpdated(): void {
|
||||
this.tree.refresh().then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().getFocusedStackFrame();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
export 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
|
||||
) {
|
||||
super(actionRunner, !!settings[WatchExpressionsView.MEMENTO], nls.localize('expressionsSection', "Expressions Section"), messageService, contextMenuService);
|
||||
this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.WATCH_EXPRESSIONS_UPDATED, (we) => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof model.Expression) {
|
||||
this.expand();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('watch', "Watch")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-watch');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService);
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.WatchExpressionsDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner),
|
||||
accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(),
|
||||
controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider)
|
||||
}, debugTreeOptions(nls.localize('watchAriaTreeLabel', "Debug Watch Expressions")));
|
||||
|
||||
this.tree.setInput(this.debugService.getModel());
|
||||
|
||||
const addWatchExpressionAction = this.instantiationService.createInstance(debugactions.AddWatchExpressionAction, debugactions.AddWatchExpressionAction.ID, debugactions.AddWatchExpressionAction.LABEL);
|
||||
const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
|
||||
const removeAllWatchExpressionsAction = this.instantiationService.createInstance(debugactions.RemoveAllWatchExpressionsAction, debugactions.RemoveAllWatchExpressionsAction.ID, debugactions.RemoveAllWatchExpressionsAction.LABEL);
|
||||
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);
|
||||
this.tree.addOneTimeListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
private onWatchExpressionsUpdated(we: model.Expression): void {
|
||||
this.tree.refresh().done(() => {
|
||||
return we instanceof model.Expression ? this.tree.reveal(we): TPromise.as(true);
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[WatchExpressionsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class CallStackView extends viewlet.CollapsibleViewletView {
|
||||
|
||||
private static MEMENTO = 'callstackview.memento';
|
||||
private pauseMessage: builder.Builder;
|
||||
private pauseMessageLabel: builder.Builder;
|
||||
|
||||
constructor(actionRunner: actions.IActionRunner, private settings: any,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(actionRunner, !!settings[CallStackView.MEMENTO], nls.localize('callstackSection', "Call Stack Section"), messageService, contextMenuService);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
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);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource),
|
||||
renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
|
||||
accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider)
|
||||
}, debugTreeOptions(nls.localize('callStackAriaLabel', "Debug Call Stack")));
|
||||
|
||||
const debugModel = this.debugService.getModel();
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
|
||||
if (!e.selection.length) {
|
||||
return;
|
||||
}
|
||||
const element = e.selection[0];
|
||||
|
||||
if (element instanceof model.StackFrame) {
|
||||
const stackFrame = <debug.IStackFrame> element;
|
||||
this.debugService.setFocusedStackFrameAndEvaluate(stackFrame);
|
||||
|
||||
const isMouse = (e.payload.origin === 'mouse');
|
||||
let preserveFocus = isMouse;
|
||||
|
||||
const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
|
||||
if (originalEvent && isMouse && originalEvent.detail === 2) {
|
||||
preserveFocus = false;
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
|
||||
this.debugService.openOrRevealEditor(stackFrame.source, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
// user clicked on 'Load More Stack Frames', get those stack frames and refresh the tree.
|
||||
if (typeof element === 'number') {
|
||||
const thread = this.debugService.getModel().getThreads()[element];
|
||||
if (thread) {
|
||||
thread.getCallStack(this.debugService, true)
|
||||
.then(() => this.tree.refresh())
|
||||
.then(() => {
|
||||
this.tree.clearFocus();
|
||||
this.tree.clearSelection();
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
|
||||
const isMouseClick = (e.payload && e.payload.origin === 'mouse');
|
||||
const isStackFrameType = (e.focus instanceof model.StackFrame);
|
||||
|
||||
if (isMouseClick && isStackFrameType) {
|
||||
this.telemetryService.publicLog('debug/callStack/selected');
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(debugModel.addListener2(debug.ModelEvents.CALLSTACK_UPDATED, () => {
|
||||
this.tree.refresh().done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => {
|
||||
const focussedThread = this.debugService.getModel().getThreads()[this.debugService.getViewModel().getFocusedThreadId()];
|
||||
if (!focussedThread) {
|
||||
this.pauseMessage.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.expand(focussedThread);
|
||||
this.tree.setFocus(this.debugService.getViewModel().getFocusedStackFrame());
|
||||
if (focussedThread.stoppedDetails && focussedThread.stoppedDetails.reason) {
|
||||
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');
|
||||
this.pauseMessage.show();
|
||||
} else {
|
||||
this.pauseMessage.hide();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[CallStackView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export 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(
|
||||
debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length),
|
||||
!!settings[BreakpointsView.MEMENTO], nls.localize('breakpointsSection', "Breakpoints Section"), messageService, contextMenuService);
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.BREAKPOINTS_UPDATED,() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(nls.localize('breakpoints', "Breakpoints")).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-breakpoints');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService);
|
||||
|
||||
this.tree = new treeimpl.Tree(this.treeContainer, {
|
||||
dataSource: new viewer.BreakpointsDataSource(),
|
||||
renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner),
|
||||
accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider),
|
||||
controller: new viewer.BreakpointsController(this.debugService, this.contextMenuService, actionProvider),
|
||||
sorter: {
|
||||
compare(tree: tree.ITree, element: any, otherElement: any): number {
|
||||
const first = <debug.IBreakpoint> element;
|
||||
const second = <debug.IBreakpoint> otherElement;
|
||||
if (first instanceof model.ExceptionBreakpoint) {
|
||||
return -1;
|
||||
}
|
||||
if (second instanceof model.ExceptionBreakpoint) {
|
||||
return 1;
|
||||
}
|
||||
if (first instanceof model.FunctionBreakpoint) {
|
||||
return -1;
|
||||
}
|
||||
if(second instanceof model.FunctionBreakpoint) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (first.source.uri.toString() !== second.source.uri.toString()) {
|
||||
return first.source.uri.toString().localeCompare(second.source.uri.toString());
|
||||
}
|
||||
|
||||
return first.desiredLineNumber - second.desiredLineNumber;
|
||||
}
|
||||
}
|
||||
}, debugTreeOptions(nls.localize('breakpointsAriaTreeLabel', "Debug Breakpoints")));
|
||||
|
||||
const debugModel = this.debugService.getModel();
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
|
||||
if (!e.selection.length) {
|
||||
return;
|
||||
}
|
||||
const element = e.selection[0];
|
||||
if (!(element instanceof model.Breakpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const breakpoint = <debug.IBreakpoint> element;
|
||||
if (!breakpoint.source.inMemory) {
|
||||
const isMouse = (e.payload.origin === 'mouse');
|
||||
let preserveFocus = isMouse;
|
||||
|
||||
const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
|
||||
if (originalEvent && isMouse && originalEvent.detail === 2) {
|
||||
preserveFocus = false;
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
|
||||
this.debugService.openOrRevealEditor(breakpoint.source, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
|
||||
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);
|
||||
}));
|
||||
}
|
||||
|
||||
public getActions(): actions.IAction[] {
|
||||
return [
|
||||
this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL),
|
||||
this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL),
|
||||
this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL)
|
||||
];
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const model = this.debugService.getModel();
|
||||
this.expandedBodySize = BreakpointsView.getExpandedBodySize(
|
||||
model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length);
|
||||
|
||||
if (this.tree) {
|
||||
this.tree.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private static getExpandedBodySize(length: number): number {
|
||||
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[BreakpointsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ import uri from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import ee = require('vs/base/common/eventEmitter');
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { AdaptiveCollapsibleViewletView, CollapsibleViewletView } from 'vs/workbench/browser/viewlet';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import editor = require('vs/editor/common/editorCommon');
|
||||
import editorbrowser = require('vs/editor/browser/editorBrowser');
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
@@ -346,6 +348,41 @@ export interface IDebugEditorContribution extends editor.IEditorContribution {
|
||||
showHover(range: editor.IEditorRange, hoveringOver: string, focus: boolean): TPromise<void>;
|
||||
}
|
||||
|
||||
// Debug view descriptors and registration
|
||||
|
||||
export class DebugViewDescriptor extends SyncDescriptor<CollapsibleViewletView | AdaptiveCollapsibleViewletView> {
|
||||
// constructor(ctorName: string, public id: string, public name: string, public cssClass: string) {
|
||||
// super(ctorName);
|
||||
// }
|
||||
}
|
||||
|
||||
export interface IDebugViewRegistry {
|
||||
registerDebugView(descriptor: DebugViewDescriptor): void;
|
||||
getDebugViews(): DebugViewDescriptor[];
|
||||
}
|
||||
|
||||
class DebugViewRegistryImpl implements IDebugViewRegistry {
|
||||
private debugViews: DebugViewDescriptor[];
|
||||
|
||||
constructor() {
|
||||
this.debugViews = [];
|
||||
}
|
||||
|
||||
public registerDebugView(descriptor: DebugViewDescriptor): void {
|
||||
if (this.debugViews.some(dsc => dsc.equals(descriptor))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.debugViews.push(descriptor);
|
||||
}
|
||||
|
||||
public getDebugViews(): DebugViewDescriptor[] {
|
||||
return this.debugViews;
|
||||
}
|
||||
}
|
||||
|
||||
export var DebugViewRegistry = <IDebugViewRegistry>new DebugViewRegistryImpl();
|
||||
|
||||
// utils
|
||||
|
||||
const _formatPIIRegexp = /{([^}]+)}/g;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions
|
||||
import wbaregistry = require('vs/workbench/common/actionRegistry');
|
||||
import viewlet = require('vs/workbench/browser/viewlet');
|
||||
import panel = require('vs/workbench/browser/panel');
|
||||
import { VariablesView, WatchExpressionsView, CallStackView, BreakpointsView } from 'vs/workbench/parts/debug/browser/debugViews';
|
||||
import wbext = require('vs/workbench/common/contributions');
|
||||
import * as debug from 'vs/workbench/parts/debug/common/debug';
|
||||
import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager';
|
||||
@@ -83,6 +84,12 @@ const openViewletKb: IKeybindings = {
|
||||
));
|
||||
(<panel.PanelRegistry>platform.Registry.as(panel.Extensions.Panels)).setDefaultPanelId(debug.REPL_ID);
|
||||
|
||||
// Register default debug views
|
||||
debug.DebugViewRegistry.registerDebugView(new debug.DebugViewDescriptor(VariablesView));
|
||||
debug.DebugViewRegistry.registerDebugView(new debug.DebugViewDescriptor(WatchExpressionsView));
|
||||
debug.DebugViewRegistry.registerDebugView(new debug.DebugViewDescriptor(CallStackView));
|
||||
debug.DebugViewRegistry.registerDebugView(new debug.DebugViewDescriptor(BreakpointsView));
|
||||
|
||||
// register action to open viewlet
|
||||
const registry = (<wbaregistry.IWorkbenchActionRegistry> platform.Registry.as(wbaregistry.Extensions.WorkbenchActions));
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), nls.localize('view', "View"));
|
||||
|
||||
@@ -126,7 +126,7 @@ export class MockDebugService extends ee.EventEmitter implements debug.IDebugSer
|
||||
}
|
||||
|
||||
public getViewModel(): debug.IViewModel {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
public openOrRevealEditor(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
|
||||
|
||||
Reference in New Issue
Block a user