mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-26 23:00:29 -04:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -1,248 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IExpression, IDebugService, IEnablement } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, FunctionBreakpoint, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITree, ContextMenuEvent, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
export const twistiePixels = 20;
|
||||
const booleanRegex = /^true|false$/i;
|
||||
const stringRegex = /^(['"]).*\1$/;
|
||||
const $ = dom.$;
|
||||
|
||||
export interface IRenderValueOptions {
|
||||
preserveWhitespace?: boolean;
|
||||
showChanged?: boolean;
|
||||
maxValueLength?: number;
|
||||
showHover?: boolean;
|
||||
colorize?: boolean;
|
||||
}
|
||||
|
||||
export interface IVariableTemplateData {
|
||||
expression: HTMLElement;
|
||||
name: HTMLElement;
|
||||
value: HTMLElement;
|
||||
}
|
||||
|
||||
export function renderViewTree(container: HTMLElement): HTMLElement {
|
||||
const treeContainer = document.createElement('div');
|
||||
dom.addClass(treeContainer, 'debug-view-content');
|
||||
container.appendChild(treeContainer);
|
||||
return treeContainer;
|
||||
}
|
||||
|
||||
function replaceWhitespace(value: string): string {
|
||||
const map: { [x: string]: string } = { '\n': '\\n', '\r': '\\r', '\t': '\\t' };
|
||||
return value.replace(/[\n\r\t]/g, char => map[char]);
|
||||
}
|
||||
|
||||
export function renderExpressionValue(expressionOrValue: IExpression | string, container: HTMLElement, options: IRenderValueOptions): void {
|
||||
let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value;
|
||||
|
||||
// remove stale classes
|
||||
container.className = 'value';
|
||||
// when resolving expressions we represent errors from the server as a variable with name === null.
|
||||
if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) {
|
||||
dom.addClass(container, 'unavailable');
|
||||
if (value !== Expression.DEFAULT_VALUE) {
|
||||
dom.addClass(container, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if (options.colorize && typeof expressionOrValue !== 'string') {
|
||||
if (expressionOrValue.type === 'number' || expressionOrValue.type === 'boolean' || expressionOrValue.type === 'string') {
|
||||
dom.addClass(container, expressionOrValue.type);
|
||||
} else if (!isNaN(+value)) {
|
||||
dom.addClass(container, 'number');
|
||||
} else if (booleanRegex.test(value)) {
|
||||
dom.addClass(container, 'boolean');
|
||||
} else if (stringRegex.test(value)) {
|
||||
dom.addClass(container, 'string');
|
||||
}
|
||||
}
|
||||
|
||||
if (options.showChanged && (<any>expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) {
|
||||
// value changed color has priority over other colors.
|
||||
container.className = 'value changed';
|
||||
}
|
||||
|
||||
if (options.maxValueLength && value.length > options.maxValueLength) {
|
||||
value = value.substr(0, options.maxValueLength) + '...';
|
||||
}
|
||||
if (value && !options.preserveWhitespace) {
|
||||
container.textContent = replaceWhitespace(value);
|
||||
} else {
|
||||
container.textContent = value;
|
||||
}
|
||||
if (options.showHover) {
|
||||
container.title = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void {
|
||||
if (variable.available) {
|
||||
data.name.textContent = replaceWhitespace(variable.name);
|
||||
data.name.title = variable.type ? variable.type : variable.name;
|
||||
dom.toggleClass(data.name, 'virtual', !!variable.presentationHint && variable.presentationHint.kind === 'virtual');
|
||||
}
|
||||
|
||||
if (variable.value) {
|
||||
data.name.textContent += variable.name ? ':' : '';
|
||||
renderExpressionValue(variable, data.value, {
|
||||
showChanged,
|
||||
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
|
||||
preserveWhitespace: false,
|
||||
showHover: true,
|
||||
colorize: true
|
||||
});
|
||||
} else {
|
||||
data.value.textContent = '';
|
||||
data.value.title = '';
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRenameBoxOptions {
|
||||
initialValue: string;
|
||||
ariaLabel: string;
|
||||
placeholder?: string;
|
||||
validationOptions?: IInputValidationOptions;
|
||||
}
|
||||
|
||||
export function renderRenameBox(debugService: IDebugService, contextViewService: IContextViewService, themeService: IThemeService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
|
||||
let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
|
||||
let inputBox = new InputBox(inputBoxContainer, contextViewService, {
|
||||
validationOptions: options.validationOptions,
|
||||
placeholder: options.placeholder,
|
||||
ariaLabel: options.ariaLabel
|
||||
});
|
||||
const styler = attachInputBoxStyler(inputBox, themeService);
|
||||
|
||||
tree.setHighlight();
|
||||
inputBox.value = options.initialValue ? options.initialValue : '';
|
||||
inputBox.focus();
|
||||
inputBox.select();
|
||||
|
||||
let disposed = false;
|
||||
const toDispose: IDisposable[] = [inputBox, styler];
|
||||
|
||||
const wrapUp = once((renamed: boolean) => {
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
if (element instanceof Expression && renamed && inputBox.value) {
|
||||
debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, onUnexpectedError);
|
||||
} else if (element instanceof Expression && !element.name) {
|
||||
debugService.removeWatchExpressions(element.getId());
|
||||
} else if (element instanceof FunctionBreakpoint && inputBox.value) {
|
||||
debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, onUnexpectedError);
|
||||
} else if (element instanceof FunctionBreakpoint && !element.name) {
|
||||
debugService.removeFunctionBreakpoints(element.getId()).done(null, onUnexpectedError);
|
||||
} else if (element instanceof Variable) {
|
||||
element.errorMessage = null;
|
||||
if (renamed && element.value !== inputBox.value) {
|
||||
element.setVariable(inputBox.value)
|
||||
// if everything went fine we need to refresh ui elements since the variable update can change watch and variables view
|
||||
.done(() => {
|
||||
tree.refresh(element, false);
|
||||
debugService.evaluateWatchExpressions();
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
tree.clearHighlight();
|
||||
tree.DOMFocus();
|
||||
tree.setFocus(element);
|
||||
|
||||
// need to remove the input box since this template will be reused.
|
||||
container.removeChild(inputBoxContainer);
|
||||
dispose(toDispose);
|
||||
}
|
||||
});
|
||||
|
||||
toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
|
||||
const isEscape = e.equals(KeyCode.Escape);
|
||||
const isEnter = e.equals(KeyCode.Enter);
|
||||
if (isEscape || isEnter) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
wrapUp(isEnter);
|
||||
}
|
||||
}));
|
||||
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
|
||||
wrapUp(true);
|
||||
}));
|
||||
}
|
||||
|
||||
export class BaseDebugController extends DefaultController {
|
||||
|
||||
private contributedContextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
private actionProvider: IActionProvider,
|
||||
menuId: MenuId,
|
||||
@IDebugService protected debugService: IDebugService,
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IMenuService menuService: IMenuService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
|
||||
this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
|
||||
}
|
||||
|
||||
public onContextMenu(tree: ITree, element: IEnablement, event: ContextMenuEvent, focusElement = true): boolean {
|
||||
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
|
||||
return false;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (focusElement) {
|
||||
tree.setFocus(element);
|
||||
}
|
||||
|
||||
if (this.actionProvider.hasSecondaryActions(tree, element)) {
|
||||
const anchor = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => {
|
||||
fillInActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions);
|
||||
return actions;
|
||||
}),
|
||||
onHide: (wasCancelled?: boolean) => {
|
||||
if (wasCancelled) {
|
||||
tree.DOMFocus();
|
||||
}
|
||||
},
|
||||
getActionsContext: () => element
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected getContext(element: any): any {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,518 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { getPathLabel } from 'vs/base/common/labels';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IEditorService, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { ViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export class BreakpointsView extends ViewsViewletPanel {
|
||||
|
||||
private static readonly MAX_VISIBLE_FILES = 9;
|
||||
private static readonly MEMENTO = 'breakopintsview.memento';
|
||||
private settings: any;
|
||||
private list: WorkbenchList<IEnablement>;
|
||||
private needsRefresh: boolean;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService);
|
||||
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
|
||||
this.settings = options.viewletSettings;
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-breakpoints');
|
||||
const delegate = new BreakpointsDelegate(this.debugService);
|
||||
|
||||
this.list = new WorkbenchList<IEnablement>(container, delegate, [
|
||||
this.instantiationService.createInstance(BreakpointsRenderer),
|
||||
new ExceptionBreakpointsRenderer(this.debugService),
|
||||
new FunctionBreakpointsRenderer(this.debugService),
|
||||
new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
|
||||
], {
|
||||
identityProvider: element => element.getId(),
|
||||
multipleSelectionSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
|
||||
CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.list.contextKeyService);
|
||||
|
||||
this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
|
||||
|
||||
const handleBreakpointFocus = (preserveFocuse: boolean, sideBySide: boolean, selectFunctionBreakpoint: boolean) => {
|
||||
const focused = this.list.getFocusedElements();
|
||||
const element = focused.length ? focused[0] : undefined;
|
||||
if (element instanceof Breakpoint) {
|
||||
openBreakpointSource(element, sideBySide, preserveFocuse, this.debugService, this.editorService).done(undefined, onUnexpectedError);
|
||||
}
|
||||
if (selectFunctionBreakpoint && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
};
|
||||
this.disposables.push(this.list.onKeyUp(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter)) {
|
||||
handleBreakpointFocus(false, event && (event.ctrlKey || event.metaKey), false);
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseDblClick(e => {
|
||||
handleBreakpointFocus(false, false, true);
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseClick(e => {
|
||||
handleBreakpointFocus(true, false, false);
|
||||
}));
|
||||
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
}
|
||||
|
||||
protected layoutBody(size: number): void {
|
||||
if (this.list) {
|
||||
this.list.layout(size);
|
||||
}
|
||||
}
|
||||
|
||||
private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
|
||||
const actions: IAction[] = [];
|
||||
const element = e.element;
|
||||
|
||||
if (element instanceof Breakpoint) {
|
||||
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editConditionalBreakpoint', "Edit Breakpoint..."), undefined, true, () => {
|
||||
return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
});
|
||||
}));
|
||||
actions.push(new Separator());
|
||||
}
|
||||
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL, this.debugService, this.keybindingService));
|
||||
|
||||
|
||||
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
|
||||
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new Separator());
|
||||
|
||||
actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
}
|
||||
|
||||
actions.push(new Separator());
|
||||
actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => TPromise.as(actions),
|
||||
getActionsContext: () => element
|
||||
});
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
return [
|
||||
new AddFunctionBreakpointAction(AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL, this.debugService, this.keybindingService),
|
||||
new ToggleBreakpointsActivatedAction(ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL, this.debugService, this.keybindingService),
|
||||
new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)
|
||||
];
|
||||
}
|
||||
|
||||
public setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
if (expanded && this.needsRefresh) {
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
}
|
||||
|
||||
public setVisible(visible: boolean): TPromise<void> {
|
||||
return super.setVisible(visible).then(() => {
|
||||
if (visible && this.needsRefresh) {
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
if (this.isExpanded() && this.isVisible()) {
|
||||
this.minimumBodySize = this.getExpandedBodySize();
|
||||
if (this.maximumBodySize < Number.POSITIVE_INFINITY) {
|
||||
this.maximumBodySize = this.minimumBodySize;
|
||||
}
|
||||
if (this.list) {
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
this.needsRefresh = false;
|
||||
}
|
||||
} else {
|
||||
this.needsRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
private get elements(): IEnablement[] {
|
||||
const model = this.debugService.getModel();
|
||||
const elements = (<IEnablement[]>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints());
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
private getExpandedBodySize(): number {
|
||||
const model = this.debugService.getModel();
|
||||
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length;
|
||||
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
|
||||
}
|
||||
}
|
||||
|
||||
class BreakpointsDelegate implements IDelegate<IEnablement> {
|
||||
|
||||
constructor(private debugService: IDebugService) {
|
||||
// noop
|
||||
}
|
||||
|
||||
getHeight(element: IEnablement): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(element: IEnablement): string {
|
||||
if (element instanceof Breakpoint) {
|
||||
return BreakpointsRenderer.ID;
|
||||
}
|
||||
if (element instanceof FunctionBreakpoint) {
|
||||
const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
|
||||
if (!element.name || (selected && selected.getId() === element.getId())) {
|
||||
return FunctionBreakpointInputRenderer.ID;
|
||||
}
|
||||
|
||||
return FunctionBreakpointsRenderer.ID;
|
||||
}
|
||||
if (element instanceof ExceptionBreakpoint) {
|
||||
return ExceptionBreakpointsRenderer.ID;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
interface IBaseBreakpointTemplateData {
|
||||
breakpoint: HTMLElement;
|
||||
name: HTMLElement;
|
||||
checkbox: HTMLInputElement;
|
||||
context: IEnablement;
|
||||
toDispose: IDisposable[];
|
||||
}
|
||||
|
||||
interface IBreakpointTemplateData extends IBaseBreakpointTemplateData {
|
||||
lineNumber: HTMLElement;
|
||||
filePath: HTMLElement;
|
||||
}
|
||||
|
||||
interface IInputTemplateData {
|
||||
inputBox: InputBox;
|
||||
breakpoint: IFunctionBreakpoint;
|
||||
reactedOnEvent: boolean;
|
||||
toDispose: IDisposable[];
|
||||
}
|
||||
|
||||
class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateData> {
|
||||
|
||||
constructor(
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'breakpoints';
|
||||
|
||||
get templateId() {
|
||||
return BreakpointsRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IBreakpointTemplateData {
|
||||
const data: IBreakpointTemplateData = Object.create(null);
|
||||
data.breakpoint = dom.append(container, $('.breakpoint'));
|
||||
|
||||
data.checkbox = <HTMLInputElement>$('input');
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose = [];
|
||||
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
|
||||
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
|
||||
}));
|
||||
|
||||
dom.append(data.breakpoint, data.checkbox);
|
||||
|
||||
data.name = dom.append(data.breakpoint, $('span.name'));
|
||||
|
||||
data.filePath = dom.append(data.breakpoint, $('span.file-path'));
|
||||
const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container'));
|
||||
data.lineNumber = dom.append(lineNumberContainer, $('span.line-number'));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
|
||||
data.context = breakpoint;
|
||||
dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
|
||||
|
||||
data.name.textContent = basename(getPathLabel(breakpoint.uri, this.contextService));
|
||||
data.lineNumber.textContent = breakpoint.lineNumber.toString();
|
||||
if (breakpoint.column) {
|
||||
data.lineNumber.textContent += `:${breakpoint.column}`;
|
||||
}
|
||||
data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.contextService, this.environmentService);
|
||||
data.checkbox.checked = breakpoint.enabled;
|
||||
|
||||
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
|
||||
if (debugActive && !breakpoint.verified) {
|
||||
dom.addClass(data.breakpoint, 'disabled');
|
||||
if (breakpoint.message) {
|
||||
data.breakpoint.title = breakpoint.message;
|
||||
}
|
||||
} else if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IBreakpointTemplateData): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IBaseBreakpointTemplateData> {
|
||||
|
||||
constructor(
|
||||
private debugService: IDebugService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'exceptionbreakpoints';
|
||||
|
||||
get templateId() {
|
||||
return ExceptionBreakpointsRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
|
||||
const data: IBreakpointTemplateData = Object.create(null);
|
||||
data.breakpoint = dom.append(container, $('.breakpoint'));
|
||||
|
||||
data.checkbox = <HTMLInputElement>$('input');
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose = [];
|
||||
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
|
||||
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
|
||||
}));
|
||||
|
||||
dom.append(data.breakpoint, data.checkbox);
|
||||
|
||||
data.name = dom.append(data.breakpoint, $('span.name'));
|
||||
dom.addClass(data.breakpoint, 'exception');
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
|
||||
data.context = exceptionBreakpoint;
|
||||
data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
|
||||
data.breakpoint.title = data.name.textContent;
|
||||
data.checkbox.checked = exceptionBreakpoint.enabled;
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBaseBreakpointTemplateData> {
|
||||
|
||||
constructor(
|
||||
private debugService: IDebugService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'functionbreakpoints';
|
||||
|
||||
get templateId() {
|
||||
return FunctionBreakpointsRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
|
||||
const data: IBreakpointTemplateData = Object.create(null);
|
||||
data.breakpoint = dom.append(container, $('.breakpoint'));
|
||||
|
||||
data.checkbox = <HTMLInputElement>$('input');
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose = [];
|
||||
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
|
||||
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
|
||||
}));
|
||||
|
||||
dom.append(data.breakpoint, data.checkbox);
|
||||
|
||||
data.name = dom.append(data.breakpoint, $('span.name'));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(functionBreakpoint: IFunctionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
|
||||
data.context = functionBreakpoint;
|
||||
data.name.textContent = functionBreakpoint.name;
|
||||
data.checkbox.checked = functionBreakpoint.enabled;
|
||||
data.breakpoint.title = functionBreakpoint.name;
|
||||
|
||||
// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
|
||||
const process = this.debugService.getViewModel().focusedProcess;
|
||||
dom.toggleClass(data.breakpoint, 'disalbed', (process && !process.session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
|
||||
if (process && !process.session.capabilities.supportsFunctionBreakpoints) {
|
||||
data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint, IInputTemplateData> {
|
||||
|
||||
constructor(
|
||||
private debugService: IDebugService,
|
||||
private contextViewService: IContextViewService,
|
||||
private themeService: IThemeService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'functionbreakpointinput';
|
||||
|
||||
get templateId() {
|
||||
return FunctionBreakpointInputRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IInputTemplateData {
|
||||
const template: IInputTemplateData = Object.create(null);
|
||||
const inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
|
||||
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
|
||||
placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"),
|
||||
ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint")
|
||||
});
|
||||
const styler = attachInputBoxStyler(inputBox, this.themeService);
|
||||
const toDispose: IDisposable[] = [inputBox, styler];
|
||||
|
||||
const wrapUp = (renamed: boolean) => {
|
||||
if (!template.reactedOnEvent) {
|
||||
template.reactedOnEvent = true;
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
|
||||
if (inputBox.value && (renamed || template.breakpoint.name)) {
|
||||
this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name).done(null, onUnexpectedError);
|
||||
} else {
|
||||
this.debugService.removeFunctionBreakpoints(template.breakpoint.getId()).done(null, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
|
||||
const isEscape = e.equals(KeyCode.Escape);
|
||||
const isEnter = e.equals(KeyCode.Enter);
|
||||
if (isEscape || isEnter) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
wrapUp(isEnter);
|
||||
}
|
||||
}));
|
||||
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
|
||||
wrapUp(true);
|
||||
}));
|
||||
|
||||
template.inputBox = inputBox;
|
||||
template.toDispose = toDispose;
|
||||
return template;
|
||||
}
|
||||
|
||||
renderElement(functionBreakpoint: IFunctionBreakpoint, index: number, data: IInputTemplateData): void {
|
||||
data.breakpoint = functionBreakpoint;
|
||||
data.reactedOnEvent = false;
|
||||
data.inputBox.value = functionBreakpoint.name || '';
|
||||
data.inputBox.focus();
|
||||
data.inputBox.select();
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IInputTemplateData): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
function openBreakpointSource(breakpoint: Breakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): TPromise<IEditor> {
|
||||
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const selection = breakpoint.endLineNumber ? {
|
||||
startLineNumber: breakpoint.lineNumber,
|
||||
endLineNumber: breakpoint.endLineNumber,
|
||||
startColumn: breakpoint.column,
|
||||
endColumn: breakpoint.endColumn
|
||||
} : {
|
||||
startLineNumber: breakpoint.lineNumber,
|
||||
startColumn: breakpoint.column || 1,
|
||||
endLineNumber: breakpoint.lineNumber,
|
||||
endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
|
||||
};
|
||||
|
||||
return editorService.openEditor({
|
||||
resource: breakpoint.uri,
|
||||
options: {
|
||||
preserveFocus,
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: !preserveFocus
|
||||
}
|
||||
}, sideBySide);
|
||||
}
|
||||
@@ -15,10 +15,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { ITree, IActionProvider, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyStackTraceAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
@@ -26,8 +24,9 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -39,18 +38,19 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
private onCallStackChangeScheduler: RunOnceScheduler;
|
||||
private settings: any;
|
||||
private needsRefresh: boolean;
|
||||
private ignoreSelectionChangedEvent: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IListService private listService: IListService
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
|
||||
@@ -95,26 +95,42 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext, {});
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new CallStackDataSource(),
|
||||
renderer: this.instantiationService.createInstance(CallStackRenderer),
|
||||
accessibilityProvider: this.instantiationService.createInstance(CallstackAccessibilityProvider),
|
||||
controller
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
this.disposables.push(this.tree.onDidChangeSelection(event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
const element = this.tree.getFocus();
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
controller.showMoreStackFrames(this.tree, element);
|
||||
} else if (element instanceof StackFrame) {
|
||||
controller.focusStackFrame(element, event, false);
|
||||
const callstackNavigator = new TreeResourceNavigator(this.tree);
|
||||
this.disposables.push(callstackNavigator);
|
||||
this.disposables.push(callstackNavigator.openResource(e => {
|
||||
if (this.ignoreSelectionChangedEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = e.element;
|
||||
if (element instanceof StackFrame) {
|
||||
this.debugService.focusStackFrame(element, element.thread, element.thread.process, true);
|
||||
element.openInEditor(this.editorService, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
if (element instanceof Thread) {
|
||||
this.debugService.focusStackFrame(undefined, element, element.process, true);
|
||||
}
|
||||
if (element instanceof Process) {
|
||||
this.debugService.focusStackFrame(undefined, undefined, element, true);
|
||||
}
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === element.processId).pop();
|
||||
const thread = process && process.getThread(element.threadId);
|
||||
if (thread) {
|
||||
(<Thread>thread).fetchCallStack()
|
||||
.done(() => this.tree.refresh(), errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -129,8 +145,14 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
this.onCallStackChangeScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
|
||||
this.updateTreeSelection().done(undefined, errors.onUnexpectedError)));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => {
|
||||
if (!this.isVisible) {
|
||||
this.needsRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateTreeSelection().done(undefined, errors.onUnexpectedError);
|
||||
}));
|
||||
|
||||
// Schedule the update of the call stack tree if the viewlet is opened after a session started #14684
|
||||
if (this.debugService.state === State.Stopped) {
|
||||
@@ -138,6 +160,13 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
}
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
private updateTreeSelection(): TPromise<void> {
|
||||
if (!this.tree.getInput()) {
|
||||
// Tree not initialized yet
|
||||
@@ -147,13 +176,22 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
const thread = this.debugService.getViewModel().focusedThread;
|
||||
const process = this.debugService.getViewModel().focusedProcess;
|
||||
const updateSelection = (element: IStackFrame | IProcess) => {
|
||||
this.ignoreSelectionChangedEvent = true;
|
||||
try {
|
||||
this.tree.setSelection([element]);
|
||||
} finally {
|
||||
this.ignoreSelectionChangedEvent = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!thread) {
|
||||
if (!process) {
|
||||
this.tree.clearSelection();
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.tree.setSelection([process]);
|
||||
updateSelection(process);
|
||||
return this.tree.reveal(process);
|
||||
}
|
||||
|
||||
@@ -162,7 +200,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.tree.setSelection([stackFrame]);
|
||||
updateSelection(stackFrame);
|
||||
return this.tree.reveal(stackFrame);
|
||||
});
|
||||
}
|
||||
@@ -182,20 +220,6 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
}
|
||||
|
||||
class CallStackController extends BaseDebugController {
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
return this.showMoreStackFrames(tree, element);
|
||||
}
|
||||
if (element instanceof StackFrame) {
|
||||
super.onLeftClick(tree, element, event);
|
||||
this.focusStackFrame(element, event, event.detail !== 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onLeftClick(tree, element, event);
|
||||
}
|
||||
|
||||
protected getContext(element: any): any {
|
||||
if (element instanceof StackFrame) {
|
||||
if (element.source.inMemory) {
|
||||
@@ -208,25 +232,6 @@ class CallStackController extends BaseDebugController {
|
||||
return element.threadId;
|
||||
}
|
||||
}
|
||||
|
||||
// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
|
||||
public showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
|
||||
const thread = process && process.getThread(threadAndProcessIds.threadId);
|
||||
if (thread) {
|
||||
(<Thread>thread).fetchCallStack()
|
||||
.done(() => tree.refresh(), errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public focusStackFrame(stackFrame: IStackFrame, event: any, preserveFocus: boolean): void {
|
||||
this.debugService.focusStackFrameAndEvaluate(stackFrame, undefined, true).then(() => {
|
||||
const sideBySide = (event && (event.ctrlKey || event.metaKey));
|
||||
return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide);
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ToggleViewletAction, Extensions as ViewletExtensions, ViewletRegistry,
|
||||
import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { VariablesView } from 'vs/workbench/parts/debug/electron-browser/variablesView';
|
||||
import { BreakpointsView } from 'vs/workbench/parts/debug/electron-browser/breakpointsView';
|
||||
import { BreakpointsView } from 'vs/workbench/parts/debug/browser/breakpointsView';
|
||||
import { WatchExpressionsView } from 'vs/workbench/parts/debug/electron-browser/watchExpressionsView';
|
||||
import { CallStackView } from 'vs/workbench/parts/debug/electron-browser/callStackView';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
@@ -38,10 +38,10 @@ import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugCont
|
||||
import 'vs/workbench/parts/debug/electron-browser/debugEditorContribution';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as debugCommands from 'vs/workbench/parts/debug/electron-browser/debugCommands';
|
||||
import { registerCommands } from 'vs/workbench/parts/debug/browser/debugCommands';
|
||||
import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/parts/debug/electron-browser/statusbarColorProvider';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/parts/debug/browser/statusbarColorProvider';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -50,6 +50,7 @@ import { Repl } from 'vs/workbench/parts/debug/electron-browser/repl';
|
||||
import { DebugQuickOpenHandler } from 'vs/workbench/parts/debug/browser/debugQuickOpen';
|
||||
import { DebugStatus } from 'vs/workbench/parts/debug/browser/debugStatus';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
||||
class OpenDebugViewletAction extends ToggleViewletAction {
|
||||
public static readonly ID = VIEWLET_ID;
|
||||
@@ -107,10 +108,10 @@ Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescri
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).setDefaultPanelId(REPL_ID);
|
||||
|
||||
// Register default debug views
|
||||
ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, size: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, size: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, size: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, size: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
|
||||
// register action to open viewlet
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions);
|
||||
@@ -201,17 +202,18 @@ configurationRegistry.registerConfiguration({
|
||||
'debug.openDebug': {
|
||||
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
|
||||
default: 'openOnFirstSessionStart',
|
||||
description: nls.localize('openDebug', "Controls whether debug viewlet should be open on debugging session start.")
|
||||
description: nls.localize('openDebug', "Controls whether debug view should be open on debugging session start.")
|
||||
},
|
||||
'launch': {
|
||||
type: 'object',
|
||||
description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces"),
|
||||
default: {}
|
||||
default: { configurations: [], compounds: [] },
|
||||
$ref: launchSchemaId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
debugCommands.registerCommands();
|
||||
registerCommands();
|
||||
|
||||
// Register Debug Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
@@ -223,7 +225,7 @@ if (isMacintosh) {
|
||||
const registerTouchBarEntry = (id: string, title: string, order, when: ContextKeyExpr, icon: string) => {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: {
|
||||
id, title, iconPath: URI.parse(require.toUrl(`vs/workbench/parts/debug/electron-browser/media/${icon}`)).fsPath
|
||||
id, title, iconPath: { dark: URI.parse(require.toUrl(`vs/workbench/parts/debug/electron-browser/media/${icon}`)).fsPath }
|
||||
},
|
||||
when,
|
||||
group: '9_debug',
|
||||
@@ -239,4 +241,4 @@ if (isMacintosh) {
|
||||
registerTouchBarEntry(StepOutAction.ID, StepOutAction.LABEL, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png');
|
||||
registerTouchBarEntry(RestartAction.ID, RestartAction.LABEL, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
|
||||
registerTouchBarEntry(StopAction.ID, StopAction.LABEL, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export function registerCommands(): void {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.logToDebugConsole',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
handler: (accessor: ServicesAccessor, value: string) => {
|
||||
if (typeof value === 'string') {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
// Use warning as severity to get the orange color for messages coming from the debug extension
|
||||
debugService.logToRepl(value, severity.Warning);
|
||||
}
|
||||
},
|
||||
when: undefined,
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.toggleBreakpoint',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5),
|
||||
when: CONTEXT_BREAKPOINTS_FOCUSED,
|
||||
primary: KeyCode.Space,
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focused = listService.lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (!(focused instanceof List)) {
|
||||
const tree = focused;
|
||||
const element = <IEnablement>tree.getFocus();
|
||||
debugService.enableOrDisableBreakpoints(!element.enabled, element).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.renameWatchExpression',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5),
|
||||
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
|
||||
primary: KeyCode.F2,
|
||||
mac: { primary: KeyCode.Enter },
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focused = listService.lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (!(focused instanceof List)) {
|
||||
const element = focused.getFocus();
|
||||
if (element instanceof Expression) {
|
||||
debugService.getViewModel().setSelectedExpression(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.setVariable',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5),
|
||||
when: CONTEXT_VARIABLES_FOCUSED,
|
||||
primary: KeyCode.F2,
|
||||
mac: { primary: KeyCode.Enter },
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focused = listService.lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (!(focused instanceof List)) {
|
||||
const element = focused.getFocus();
|
||||
if (element instanceof Variable) {
|
||||
debugService.getViewModel().setSelectedExpression(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.removeWatchExpression',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
|
||||
primary: KeyCode.Delete,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focused = listService.lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (!(focused instanceof List)) {
|
||||
const element = focused.getFocus();
|
||||
if (element instanceof Expression) {
|
||||
debugService.removeWatchExpressions(element.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.removeBreakpoint',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: CONTEXT_BREAKPOINTS_FOCUSED,
|
||||
primary: KeyCode.Delete,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focused = listService.lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (!(focused instanceof List)) {
|
||||
const element = focused.getFocus();
|
||||
if (element instanceof Breakpoint) {
|
||||
debugService.removeBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
|
||||
} else if (element instanceof FunctionBreakpoint) {
|
||||
debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.installAdditionalDebuggers',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: (accessor) => {
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
return viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search('tag:debuggers @sort:installs');
|
||||
viewlet.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.addConfiguration',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: (accessor, workspaceUri: string) => {
|
||||
const manager = accessor.get(IDebugService).getConfigurationManager();
|
||||
if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
const launch = manager.getLaunches().filter(l => l.workspace.uri.toString() === workspaceUri).pop() || manager.selectedLaunch;
|
||||
|
||||
return launch.openConfigFile(false).done(editor => {
|
||||
if (editor) {
|
||||
const codeEditor = <ICodeEditor>editor.getControl();
|
||||
if (codeEditor) {
|
||||
return codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -14,25 +14,27 @@ import * as objects from 'vs/base/common/objects';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import * as extensionsRegistry from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch, IAdapterExecutable } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
|
||||
// debuggers extension point
|
||||
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawAdapter[]>('debuggers', [], {
|
||||
@@ -102,11 +104,11 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE
|
||||
}
|
||||
},
|
||||
osx: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx', "OS X specific settings."),
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx', "macOS specific settings."),
|
||||
type: 'object',
|
||||
properties: {
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for OSX."),
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for macOS."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
@@ -147,14 +149,12 @@ const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtens
|
||||
});
|
||||
|
||||
// debug general schema
|
||||
|
||||
export const schemaId = 'vscode://schemas/launch';
|
||||
const defaultCompound: ICompound = { name: 'Compound', configurations: [] };
|
||||
const schema: IJSONSchema = {
|
||||
id: schemaId,
|
||||
id: launchSchemaId,
|
||||
type: 'object',
|
||||
title: nls.localize('app.launch.json.title', "Launch"),
|
||||
required: ['version', 'configurations'],
|
||||
required: [],
|
||||
default: { version: '0.2.0', configurations: [], compounds: [] },
|
||||
properties: {
|
||||
version: {
|
||||
@@ -186,7 +186,23 @@ const schema: IJSONSchema = {
|
||||
type: 'array',
|
||||
default: [],
|
||||
items: {
|
||||
type: 'string'
|
||||
oneOf: [{
|
||||
enum: [],
|
||||
description: nls.localize('useUniqueNames', "Please use unique configuration names.")
|
||||
}, {
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
properties: {
|
||||
name: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.name', "Name of compound. Appears in the launch configuration drop down menu.")
|
||||
},
|
||||
folder: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.folder', "Name of folder in which the compound is located.")
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.")
|
||||
}
|
||||
@@ -201,7 +217,7 @@ const schema: IJSONSchema = {
|
||||
};
|
||||
|
||||
const jsonRegistry = <IJSONContributionRegistry>Registry.as(JSONExtensions.JSONContribution);
|
||||
jsonRegistry.registerSchema(schemaId, schema);
|
||||
jsonRegistry.registerSchema(launchSchemaId, schema);
|
||||
const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
|
||||
const DEBUG_SELECTED_ROOT = 'debug.selectedroot';
|
||||
|
||||
@@ -209,8 +225,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
private adapters: Adapter[];
|
||||
private breakpointModeIdsSet = new Set<string>();
|
||||
private launches: ILaunch[];
|
||||
private _selectedName: string;
|
||||
private _selectedLaunch: ILaunch;
|
||||
private selectedName: string;
|
||||
private selectedLaunch: ILaunch;
|
||||
private toDispose: IDisposable[];
|
||||
private _onDidSelectConfigurationName = new Emitter<void>();
|
||||
private providers: IDebugConfigurationProvider[];
|
||||
@@ -232,7 +248,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
this.registerListeners(lifecycleService);
|
||||
this.initLaunches();
|
||||
const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
|
||||
const filtered = this.launches.filter(l => l.workspace.uri.toString() === previousSelectedRoot);
|
||||
const filtered = this.launches.filter(l => l.uri.toString() === previousSelectedRoot);
|
||||
this.selectConfiguration(filtered.length ? filtered[0] : undefined, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
|
||||
}
|
||||
|
||||
@@ -276,6 +292,14 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
.then(results => results.reduce((first, second) => first.concat(second), []));
|
||||
}
|
||||
|
||||
public debugAdapterExecutable(folderUri: uri | undefined, type: string): TPromise<IAdapterExecutable | undefined> {
|
||||
const providers = this.providers.filter(p => p.type === type && p.debugAdapterExecutable);
|
||||
if (providers.length === 1) {
|
||||
return providers[0].debugAdapterExecutable(folderUri);
|
||||
}
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
private registerListeners(lifecycleService: ILifecycleService): void {
|
||||
debuggersExtPoint.setHandler((extensions) => {
|
||||
extensions.forEach(extension => {
|
||||
@@ -293,7 +317,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
if (duplicate) {
|
||||
duplicate.merge(rawAdapter, extension.description);
|
||||
} else {
|
||||
this.adapters.push(new Adapter(rawAdapter, extension.description, this.configurationService, this.commandService));
|
||||
this.adapters.push(new Adapter(this, rawAdapter, extension.description, this.configurationService, this.commandService));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -310,6 +334,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
items.defaultSnippets.push(...configurationSnippets);
|
||||
}
|
||||
});
|
||||
|
||||
this.setCompoundSchemaValues();
|
||||
});
|
||||
|
||||
breakpointsExtPoint.setHandler(extensions => {
|
||||
@@ -323,10 +349,12 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => {
|
||||
this.initLaunches();
|
||||
this.selectConfiguration();
|
||||
this.setCompoundSchemaValues();
|
||||
}));
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('launch')) {
|
||||
this.selectConfiguration();
|
||||
this.setCompoundSchemaValues();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -335,42 +363,75 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
private initLaunches(): void {
|
||||
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder));
|
||||
if (this.launches.indexOf(this._selectedLaunch) === -1) {
|
||||
this._selectedLaunch = undefined;
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this));
|
||||
}
|
||||
this.launches.push(this.instantiationService.createInstance(UserLaunch, this));
|
||||
|
||||
if (this.launches.indexOf(this.selectedLaunch) === -1) {
|
||||
this.selectedLaunch = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private setCompoundSchemaValues(): void {
|
||||
const compoundConfigurationsSchema = (<IJSONSchema>schema.properties['compounds'].items).properties['configurations'];
|
||||
const launchNames = this.launches.map(l =>
|
||||
l.getConfigurationNames(false)).reduce((first, second) => first.concat(second), []);
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[0].enum = launchNames;
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[1].properties.name.enum = launchNames;
|
||||
|
||||
const folderNames = this.contextService.getWorkspace().folders.map(f => f.name);
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[1].properties.folder.enum = folderNames;
|
||||
|
||||
jsonRegistry.registerSchema(launchSchemaId, schema);
|
||||
}
|
||||
|
||||
public getLaunches(): ILaunch[] {
|
||||
return this.launches;
|
||||
}
|
||||
|
||||
public get selectedLaunch(): ILaunch {
|
||||
return this._selectedLaunch;
|
||||
public getLaunch(workspaceUri: uri): ILaunch {
|
||||
if (!uri.isUri(workspaceUri)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.launches.filter(l => l.workspace && l.workspace.uri.toString() === workspaceUri.toString()).pop();
|
||||
}
|
||||
|
||||
public get selectedName(): string {
|
||||
return this._selectedName;
|
||||
public get selectedConfiguration(): { launch: ILaunch, name: string } {
|
||||
return {
|
||||
launch: this.selectedLaunch,
|
||||
name: this.selectedName
|
||||
};
|
||||
}
|
||||
|
||||
public get onDidSelectConfiguration(): Event<void> {
|
||||
return this._onDidSelectConfigurationName.event;
|
||||
}
|
||||
|
||||
public getWorkspaceLaunch(): ILaunch {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
return this.launches[this.launches.length - 1];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public selectConfiguration(launch?: ILaunch, name?: string, debugStarted?: boolean): void {
|
||||
const previousLaunch = this._selectedLaunch;
|
||||
const previousName = this._selectedName;
|
||||
const previousLaunch = this.selectedLaunch;
|
||||
const previousName = this.selectedName;
|
||||
|
||||
if (!launch) {
|
||||
launch = this.selectedLaunch && this.selectedLaunch.getConfigurationNames().length ? this.selectedLaunch : first(this.launches, l => !!l.getConfigurationNames().length, this.launches.length ? this.launches[0] : undefined);
|
||||
}
|
||||
|
||||
this._selectedLaunch = launch;
|
||||
this.selectedLaunch = launch;
|
||||
const names = launch ? launch.getConfigurationNames() : [];
|
||||
if (name && names.indexOf(name) >= 0) {
|
||||
this._selectedName = name;
|
||||
this.selectedName = name;
|
||||
}
|
||||
if (names.indexOf(this.selectedName) === -1) {
|
||||
this._selectedName = names.length ? names[0] : undefined;
|
||||
this.selectedName = names.length ? names[0] : undefined;
|
||||
}
|
||||
|
||||
if (this.selectedLaunch !== previousLaunch || this.selectedName !== previousName) {
|
||||
@@ -378,10 +439,10 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
}
|
||||
|
||||
public canSetBreakpointsIn(model: IModel): boolean {
|
||||
public canSetBreakpointsIn(model: ITextModel): boolean {
|
||||
const modeId = model ? model.getLanguageIdentifier().language : null;
|
||||
if (!modeId || modeId === 'jsonc') {
|
||||
// do not allow breakpoints in our settings files
|
||||
if (!modeId || modeId === 'jsonc' || modeId === 'log') {
|
||||
// do not allow breakpoints in our settings files and output
|
||||
return false;
|
||||
}
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').allowBreakpointsEverywhere) {
|
||||
@@ -403,6 +464,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
let candidates: Adapter[];
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
@@ -412,10 +474,16 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
if (adapters.length === 1) {
|
||||
return TPromise.as(adapters[0]);
|
||||
}
|
||||
if (adapters.length > 1) {
|
||||
candidates = adapters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.quickOpenService.pick([...this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider), { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
|
||||
if (!candidates) {
|
||||
candidates = this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider);
|
||||
}
|
||||
return this.quickOpenService.pick([...candidates, { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
|
||||
.then(picked => {
|
||||
if (picked instanceof Adapter) {
|
||||
return picked;
|
||||
@@ -430,8 +498,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
private store(): void {
|
||||
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
|
||||
if (this._selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspace.uri.toString(), StorageScope.WORKSPACE);
|
||||
if (this.selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,15 +514,32 @@ class Launch implements ILaunch {
|
||||
private configurationManager: ConfigurationManager,
|
||||
public workspace: IWorkspaceFolder,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public get uri(): uri {
|
||||
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.workspace.name;
|
||||
}
|
||||
|
||||
public get hidden(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch', { resource: this.workspace.uri }).workspaceFolder;
|
||||
}
|
||||
|
||||
public getCompound(name: string): ICompound {
|
||||
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
const config = this.getConfig();
|
||||
if (!config || !config.compounds) {
|
||||
return null;
|
||||
}
|
||||
@@ -462,13 +547,13 @@ class Launch implements ILaunch {
|
||||
return config.compounds.filter(compound => compound.name === name).pop();
|
||||
}
|
||||
|
||||
public getConfigurationNames(): string[] {
|
||||
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
public getConfigurationNames(includeCompounds = true): string[] {
|
||||
const config = this.getConfig();
|
||||
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
|
||||
return [];
|
||||
} else {
|
||||
const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name);
|
||||
if (names.length > 0 && config.compounds) {
|
||||
if (includeCompounds && config.compounds) {
|
||||
if (config.compounds) {
|
||||
names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)
|
||||
.map(compound => compound.name));
|
||||
@@ -480,7 +565,8 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public getConfiguration(name: string): IConfig {
|
||||
const config = objects.deepClone(this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri }));
|
||||
// We need to clone the configuration in order to be able to make changes to it #42198
|
||||
const config = objects.deepClone(this.getConfig());
|
||||
if (!config || !config.configurations) {
|
||||
return null;
|
||||
}
|
||||
@@ -488,6 +574,18 @@ class Launch implements ILaunch {
|
||||
return config.configurations.filter(config => config && config.name === name).shift();
|
||||
}
|
||||
|
||||
protected getWorkspaceForResolving(): IWorkspaceFolder {
|
||||
if (this.workspace) {
|
||||
return this.workspace;
|
||||
}
|
||||
|
||||
if (this.contextService.getWorkspace().folders.length === 1) {
|
||||
return this.contextService.getWorkspace().folders[0];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public resolveConfiguration(config: IConfig): TPromise<IConfig> {
|
||||
const result = objects.deepClone(config) as IConfig;
|
||||
// Set operating system specific properties #1873
|
||||
@@ -504,22 +602,18 @@ class Launch implements ILaunch {
|
||||
|
||||
// massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths.
|
||||
Object.keys(result).forEach(key => {
|
||||
result[key] = this.configurationResolverService.resolveAny(this.workspace, result[key]);
|
||||
result[key] = this.configurationResolverService.resolveAny(this.getWorkspaceForResolving(), result[key]);
|
||||
});
|
||||
|
||||
const adapter = this.configurationManager.getAdapter(result.type);
|
||||
return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null);
|
||||
}
|
||||
|
||||
public get uri(): uri {
|
||||
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
|
||||
}
|
||||
|
||||
public openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
const resource = this.uri;
|
||||
let configFileCreated = false;
|
||||
let pinned = false;
|
||||
|
||||
return this.fileService.resolveContent(resource).then(content => content, err => {
|
||||
return this.fileService.resolveContent(resource).then(content => content.value, err => {
|
||||
|
||||
// launch.json not found: create one by collecting launch configs from debugConfigProviders
|
||||
|
||||
@@ -537,20 +631,20 @@ class Launch implements ILaunch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
configFileCreated = true;
|
||||
pinned = true; // pin only if config file is created #8727
|
||||
return this.fileService.updateContent(resource, content).then(() => {
|
||||
// convert string into IContent; see #32135
|
||||
return { value: content };
|
||||
return content;
|
||||
});
|
||||
});
|
||||
}).then(content => {
|
||||
if (!content) {
|
||||
return undefined;
|
||||
}
|
||||
const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`);
|
||||
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (content.value.charAt(i) === '\n') {
|
||||
if (content.charAt(i) === '\n') {
|
||||
startLineNumber++;
|
||||
}
|
||||
}
|
||||
@@ -561,7 +655,7 @@ class Launch implements ILaunch {
|
||||
options: {
|
||||
forceOpen: true,
|
||||
selection,
|
||||
pinned: configFileCreated, // pin only if config file is created #8727
|
||||
pinned,
|
||||
revealIfVisible: true
|
||||
},
|
||||
}, sideBySide);
|
||||
@@ -570,3 +664,68 @@ class Launch implements ILaunch {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceLaunch extends Launch implements ILaunch {
|
||||
|
||||
constructor(
|
||||
configurationManager: ConfigurationManager,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(configurationManager, undefined, fileService, editorService, configurationService, configurationResolverService, contextService);
|
||||
}
|
||||
|
||||
get uri(): uri {
|
||||
return this.contextService.getWorkspace().configuration;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return nls.localize('workspace', "workspace");
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').workspace;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration });
|
||||
}
|
||||
}
|
||||
|
||||
class UserLaunch extends Launch implements ILaunch {
|
||||
|
||||
constructor(
|
||||
configurationManager: ConfigurationManager,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(configurationManager, undefined, fileService, editorService, configurationService, configurationResolverService, contextService);
|
||||
}
|
||||
|
||||
get uri(): uri {
|
||||
return this.preferencesService.userSettingsResource;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return nls.localize('user settings', "user settings");
|
||||
}
|
||||
|
||||
public get hidden(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').user;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
return this.preferencesService.openGlobalSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IDecorationOptions, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -27,7 +28,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { DebugHoverWidget } from 'vs/workbench/parts/debug/electron-browser/debugHover';
|
||||
import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpointAction, DisableBreakpointAction, AddConditionalBreakpointAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -40,7 +41,7 @@ import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
@@ -77,13 +78,12 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@ICodeEditorService private codeEditorService: ICodeEditorService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IListService listService: IListService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
this.breakpointHintDecoration = [];
|
||||
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService, themeService, contextKeyService, listService);
|
||||
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService, themeService);
|
||||
this.toDispose = [];
|
||||
this.showHoverScheduler = new RunOnceScheduler(() => this.showHover(this.hoverRange, false), HOVER_DELAY);
|
||||
this.hideHoverScheduler = new RunOnceScheduler(() => this.hoverWidget.hide(), HOVER_DELAY);
|
||||
@@ -409,12 +409,13 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
// configuration widget
|
||||
private updateConfigurationWidgetVisibility(): void {
|
||||
const model = this.editor.getModel();
|
||||
if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
if (model && LAUNCH_JSON_REGEX.test(model.uri.toString())) {
|
||||
this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null);
|
||||
this.configurationWidget.render();
|
||||
this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration().done(undefined, errors.onUnexpectedError)));
|
||||
} else if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +469,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
|
||||
private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint-glyph',
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
@@ -581,6 +582,10 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
if (!this.wordToLineNumbersMap) {
|
||||
this.wordToLineNumbersMap = new Map<string, Position[]>();
|
||||
const model = this.editor.getModel();
|
||||
if (!model) {
|
||||
return this.wordToLineNumbersMap;
|
||||
}
|
||||
|
||||
// For every word in every line, map its ranges for fast lookup
|
||||
for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
@@ -592,11 +597,14 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
model.forceTokenization(lineNumber);
|
||||
const lineTokens = model.getLineTokens(lineNumber);
|
||||
for (let token = lineTokens.firstToken(); !!token; token = token.next()) {
|
||||
const tokenStr = lineContent.substring(token.startOffset, token.endOffset);
|
||||
for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset);
|
||||
|
||||
// Token is a word and not a comment
|
||||
if (token.tokenType === StandardTokenType.Other) {
|
||||
if (tokenType === StandardTokenType.Other) {
|
||||
DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match
|
||||
const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr);
|
||||
|
||||
@@ -606,7 +614,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.wordToLineNumbersMap.set(word, []);
|
||||
}
|
||||
|
||||
this.wordToLineNumbersMap.get(word).push(new Position(lineNumber, token.startOffset));
|
||||
this.wordToLineNumbersMap.get(word).push(new Position(lineNumber, tokenStartOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { DefaultController, ICancelableEvent, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ICancelableEvent, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -19,14 +19,14 @@ import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPosit
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { renderExpressionValue } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { renderExpressionValue } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { VariablesDataSource, VariablesRenderer } from 'vs/workbench/parts/debug/electron-browser/variablesView';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
const MAX_ELEMENTS_SHOWN = 18;
|
||||
@@ -54,9 +54,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
private editor: ICodeEditor,
|
||||
private debugService: IDebugService,
|
||||
private instantiationService: IInstantiationService,
|
||||
private themeService: IThemeService,
|
||||
private contextKeyService: IContextKeyService,
|
||||
private listService: IListService
|
||||
private themeService: IThemeService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
|
||||
@@ -71,16 +69,15 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
this.complexValueTitle = dom.append(this.complexValueContainer, $('.title'));
|
||||
this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree'));
|
||||
this.treeContainer.setAttribute('role', 'tree');
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new VariablesDataSource(),
|
||||
renderer: this.instantiationService.createInstance(VariablesHoverRenderer),
|
||||
controller: new DebugHoverController(this.editor)
|
||||
controller: this.instantiationService.createInstance(DebugHoverController, this.editor)
|
||||
}, {
|
||||
indentPixels: 6,
|
||||
twistiePixels: 15,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"),
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover")
|
||||
});
|
||||
|
||||
this.valueContainer = $('.value');
|
||||
this.valueContainer.tabIndex = 0;
|
||||
@@ -337,10 +334,13 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DebugHoverController extends DefaultController {
|
||||
class DebugHoverController extends WorkbenchTreeController {
|
||||
|
||||
constructor(private editor: ICodeEditor) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ openMode: OpenMode.SINGLE_CLICK }, configurationService);
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin = 'mouse'): boolean {
|
||||
|
||||
@@ -11,7 +11,6 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { first, distinct } from 'vs/base/common/arrays';
|
||||
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
@@ -22,10 +21,9 @@ import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
@@ -37,7 +35,7 @@ import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression,
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
|
||||
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
|
||||
import Constants from 'vs/workbench/parts/markers/common/constants';
|
||||
import { ITaskService, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
|
||||
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
|
||||
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
|
||||
@@ -53,6 +51,10 @@ import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-br
|
||||
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { TaskEvent, TaskEventKind } from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
|
||||
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
|
||||
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
|
||||
@@ -88,7 +90,8 @@ export class DebugService implements debug.IDebugService {
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IPanelService private panelService: IPanelService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IBroadcastService private broadcastService: IBroadcastService,
|
||||
@@ -122,7 +125,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
|
||||
this.loadExceptionBreakpoints(), this.loadWatchExpressions());
|
||||
this.toDispose.push(this.model);
|
||||
this.viewModel = new ViewModel();
|
||||
this.viewModel = new ViewModel(contextKeyService);
|
||||
this.firstSessionStart = true;
|
||||
|
||||
this.registerListeners();
|
||||
@@ -252,7 +255,7 @@ export class DebugService implements debug.IDebugService {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(stackFrameToFocus);
|
||||
if (thread.stoppedDetails) {
|
||||
this.windowService.focusWindow();
|
||||
aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
|
||||
@@ -274,7 +277,7 @@ export class DebugService implements debug.IDebugService {
|
||||
if (session) {
|
||||
session.disconnect().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
this.messageService.show(severity.Error, e.message);
|
||||
this.notificationService.error(e.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -309,7 +312,7 @@ export class DebugService implements debug.IDebugService {
|
||||
aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
|
||||
if (session && session.getId() === event.sessionId) {
|
||||
if (event.body && event.body.restart && process) {
|
||||
this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message));
|
||||
this.restartProcess(process, event.body.restart).done(null, err => this.notificationService.error(err.message));
|
||||
} else {
|
||||
session.disconnect().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
@@ -320,7 +323,7 @@ export class DebugService implements debug.IDebugService {
|
||||
const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
|
||||
this.model.clearThreads(session.getId(), false, threadId);
|
||||
if (this.viewModel.focusedProcess.getId() === session.getId()) {
|
||||
this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(undefined, this.viewModel.focusedThread, this.viewModel.focusedProcess);
|
||||
}
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Running);
|
||||
}));
|
||||
@@ -534,21 +537,34 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
}
|
||||
|
||||
public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
|
||||
public focusStackFrame(stackFrame: debug.IStackFrame, thread?: debug.IThread, process?: debug.IProcess, explicit?: boolean): void {
|
||||
if (!process) {
|
||||
const processes = this.model.getProcesses();
|
||||
process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null;
|
||||
if (stackFrame || thread) {
|
||||
process = stackFrame ? stackFrame.thread.process : thread.process;
|
||||
} else {
|
||||
const processes = this.model.getProcesses();
|
||||
process = processes.length ? processes[0] : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!thread) {
|
||||
if (stackFrame) {
|
||||
thread = stackFrame.thread;
|
||||
} else {
|
||||
const threads = process ? process.getAllThreads() : undefined;
|
||||
thread = threads && threads.length ? threads[0] : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stackFrame) {
|
||||
const threads = process ? process.getAllThreads() : null;
|
||||
const callStack = threads && threads.length === 1 ? threads[0].getCallStack() : null;
|
||||
stackFrame = callStack && callStack.length ? callStack[0] : null;
|
||||
if (thread) {
|
||||
const callStack = thread.getCallStack();
|
||||
stackFrame = callStack && callStack.length ? callStack[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
|
||||
this.viewModel.setFocus(stackFrame, thread, process, explicit);
|
||||
this.updateStateAndEmit();
|
||||
|
||||
return this.model.evaluateWatchExpressions(process, stackFrame);
|
||||
}
|
||||
|
||||
public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
|
||||
@@ -567,16 +583,20 @@ export class DebugService implements debug.IDebugService {
|
||||
return this.sendAllBreakpoints();
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: debug.IBreakpointData[]): TPromise<void> {
|
||||
this.model.addBreakpoints(uri, rawBreakpoints);
|
||||
rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath)));
|
||||
|
||||
return this.sendBreakpoints(uri);
|
||||
}
|
||||
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void> {
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }, sendOnResourceSaved: boolean): void {
|
||||
this.model.updateBreakpoints(data);
|
||||
return this.sendBreakpoints(uri);
|
||||
if (sendOnResourceSaved) {
|
||||
this.breakpointsToSendOnResourceSaved.add(uri.toString());
|
||||
} else {
|
||||
this.sendBreakpoints(uri);
|
||||
}
|
||||
}
|
||||
|
||||
public removeBreakpoints(id?: string): TPromise<any> {
|
||||
@@ -594,8 +614,8 @@ export class DebugService implements debug.IDebugService {
|
||||
return this.sendAllBreakpoints();
|
||||
}
|
||||
|
||||
public addFunctionBreakpoint(): void {
|
||||
const newFunctionBreakpoint = this.model.addFunctionBreakpoint('');
|
||||
public addFunctionBreakpoint(name?: string, id?: string): void {
|
||||
const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id);
|
||||
this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint);
|
||||
}
|
||||
|
||||
@@ -612,7 +632,7 @@ export class DebugService implements debug.IDebugService {
|
||||
public addReplExpression(name: string): TPromise<void> {
|
||||
return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
|
||||
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
|
||||
.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
|
||||
.then(() => this.focusStackFrame(this.viewModel.focusedStackFrame, this.viewModel.focusedThread, this.viewModel.focusedProcess));
|
||||
}
|
||||
|
||||
public removeReplExpressions(): void {
|
||||
@@ -628,11 +648,12 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
}
|
||||
|
||||
public addWatchExpression(name: string): TPromise<void> {
|
||||
return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
|
||||
public addWatchExpression(name: string): void {
|
||||
const we = this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
|
||||
this.viewModel.setSelectedExpression(we);
|
||||
}
|
||||
|
||||
public renameWatchExpression(id: string, newName: string): TPromise<void> {
|
||||
public renameWatchExpression(id: string, newName: string): void {
|
||||
return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
|
||||
}
|
||||
|
||||
@@ -644,14 +665,10 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.removeWatchExpressions(id);
|
||||
}
|
||||
|
||||
public evaluateWatchExpressions(): TPromise<void> {
|
||||
return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
|
||||
}
|
||||
|
||||
public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
|
||||
public startDebugging(launch: debug.ILaunch, configOrName?: debug.IConfig | string, noDebug = false): TPromise<any> {
|
||||
|
||||
// make sure to save all files and that the configuration is up to date
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() =>
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() =>
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
if (this.model.getProcesses().length === 0) {
|
||||
this.removeReplExpressions();
|
||||
@@ -659,12 +676,10 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.getBreakpoints().forEach(bp => bp.verified = false);
|
||||
}
|
||||
this.launchJsonChanged = false;
|
||||
const manager = this.getConfigurationManager();
|
||||
const launch = root ? manager.getLaunches().filter(l => l.workspace.uri.toString() === root.uri.toString()).pop() : undefined;
|
||||
|
||||
let config: debug.IConfig, compound: debug.ICompound;
|
||||
if (!configOrName) {
|
||||
configOrName = this.configurationManager.selectedName;
|
||||
configOrName = this.configurationManager.selectedConfiguration.name;
|
||||
}
|
||||
if (typeof configOrName === 'string' && launch) {
|
||||
config = launch.getConfiguration(configOrName);
|
||||
@@ -672,10 +687,6 @@ export class DebugService implements debug.IDebugService {
|
||||
} else if (typeof configOrName !== 'string') {
|
||||
config = configOrName;
|
||||
}
|
||||
if (launch) {
|
||||
// in the drop down the name of the top most compound takes precedence over the launch config name
|
||||
manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true);
|
||||
}
|
||||
|
||||
if (compound) {
|
||||
if (!compound.configurations) {
|
||||
@@ -683,7 +694,35 @@ export class DebugService implements debug.IDebugService {
|
||||
"Compound must have \"configurations\" attribute set in order to start multiple configurations.")));
|
||||
}
|
||||
|
||||
return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
|
||||
return TPromise.join(compound.configurations.map(configData => {
|
||||
const name = typeof configData === 'string' ? configData : configData.name;
|
||||
if (name === compound.name) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
let launchForName: debug.ILaunch;
|
||||
if (typeof configData === 'string') {
|
||||
const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
|
||||
if (launchesContainingName.length === 1) {
|
||||
launchForName = launchesContainingName[0];
|
||||
} else if (launchesContainingName.length > 1 && launchesContainingName.indexOf(launch) >= 0) {
|
||||
// If there are multiple launches containing the configuration give priority to the configuration in the current launch
|
||||
launchForName = launch;
|
||||
} else {
|
||||
return TPromise.wrapError(new Error(launchesContainingName.length === 0 ? nls.localize('noConfigurationNameInWorkspace', "Could not find launch configuration '{0}' in the workspace.", name)
|
||||
: nls.localize('multipleConfigurationNamesInWorkspace', "There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name)));
|
||||
}
|
||||
} else if (configData.folder) {
|
||||
const launchesMatchingConfigData = this.configurationManager.getLaunches().filter(l => l.workspace && l.workspace.name === configData.folder && !!l.getConfiguration(configData.name));
|
||||
if (launchesMatchingConfigData.length === 1) {
|
||||
launchForName = launchesMatchingConfigData[0];
|
||||
} else {
|
||||
return TPromise.wrapError(new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound.name)));
|
||||
}
|
||||
}
|
||||
|
||||
return this.startDebugging(launchForName, name, noDebug);
|
||||
}));
|
||||
}
|
||||
if (configOrName && !config) {
|
||||
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName) :
|
||||
@@ -714,16 +753,15 @@ export class DebugService implements debug.IDebugService {
|
||||
|
||||
return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() =>
|
||||
(type ? this.extensionService.activateByEvent(`onDebugResolve:${type}`) : TPromise.as(null)).then(() =>
|
||||
this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => {
|
||||
this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config).then(config => {
|
||||
// a falsy config indicates an aborted launch
|
||||
if (config && config.type) {
|
||||
return this.createProcess(root, config, sessionId);
|
||||
}
|
||||
if (launch) {
|
||||
return launch.openConfigFile(false, type).then(editor => undefined);
|
||||
return this.createProcess(launch, config, sessionId);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
if (launch) {
|
||||
return launch.openConfigFile(false, type).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
})
|
||||
).then(() => wrapUpState(), err => {
|
||||
wrapUpState();
|
||||
@@ -733,9 +771,9 @@ export class DebugService implements debug.IDebugService {
|
||||
)));
|
||||
}
|
||||
|
||||
private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<void> {
|
||||
private createProcess(launch: debug.ILaunch, config: debug.IConfig, sessionId: string): TPromise<void> {
|
||||
return this.textFileService.saveAll().then(() =>
|
||||
(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
|
||||
(launch ? launch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
|
||||
if (!resolvedConfig) {
|
||||
// User canceled resolving of interactive variables, silently return
|
||||
return undefined;
|
||||
@@ -744,65 +782,50 @@ export class DebugService implements debug.IDebugService {
|
||||
if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
|
||||
let message: string;
|
||||
if (config.request !== 'attach' && config.request !== 'launch') {
|
||||
message = config.request ? nls.localize('debugRequestNotSupported', "Attribute `{0}` has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
|
||||
message = config.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
|
||||
: nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request');
|
||||
|
||||
} else {
|
||||
message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
|
||||
nls.localize('debugTypeMissing', "Missing property `type` for the chosen launch configuration.");
|
||||
nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
|
||||
}
|
||||
|
||||
return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
|
||||
return this.showError(message);
|
||||
}
|
||||
|
||||
this.toDisposeOnSessionEnd.set(sessionId, []);
|
||||
const debugAnywayAction = new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
|
||||
this.messageService.hideAll();
|
||||
return this.doCreateProcess(root, resolvedConfig, sessionId);
|
||||
|
||||
const workspace = launch ? launch.workspace : undefined;
|
||||
const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => {
|
||||
return this.doCreateProcess(workspace, resolvedConfig, sessionId);
|
||||
});
|
||||
|
||||
return this.runPreLaunchTask(sessionId, root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
|
||||
return this.runPreLaunchTask(sessionId, workspace, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
|
||||
const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
|
||||
const successExitCode = taskSummary && taskSummary.exitCode === 0;
|
||||
const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
|
||||
if (successExitCode || (errorCount === 0 && !failureExitCode)) {
|
||||
return this.doCreateProcess(root, resolvedConfig, sessionId);
|
||||
return this.doCreateProcess(workspace, resolvedConfig, sessionId);
|
||||
}
|
||||
|
||||
this.messageService.show(severity.Error, {
|
||||
message: errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode),
|
||||
actions: [
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
|
||||
CloseAction
|
||||
]
|
||||
const message = errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode);
|
||||
|
||||
const showErrorsAction = new Action('debug.showErrors', nls.localize('showErrors', "Show Errors"), undefined, true, () => {
|
||||
return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => undefined);
|
||||
});
|
||||
return undefined;
|
||||
|
||||
return this.showError(message, [debugAnywayAction, showErrorsAction]);
|
||||
}, (err: TaskError) => {
|
||||
this.messageService.show(err.severity, {
|
||||
message: err.message,
|
||||
actions: [
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
|
||||
this.taskService.configureAction(),
|
||||
CloseAction
|
||||
]
|
||||
});
|
||||
return this.showError(err.message, [debugAnywayAction, this.taskService.configureAction()]);
|
||||
});
|
||||
}, err => {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
|
||||
return undefined;
|
||||
return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
|
||||
}
|
||||
|
||||
return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
|
||||
if (openend) {
|
||||
this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
return launch && launch.openConfigFile(false).then(editor => void 0);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -870,8 +893,8 @@ export class DebugService implements debug.IDebugService {
|
||||
if (session.disconnected) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
this.focusStackFrame(undefined, undefined, process);
|
||||
this._onDidNewProcess.fire(process);
|
||||
this.focusStackFrameAndEvaluate(null, process);
|
||||
|
||||
const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getValue<debug.IDebugConfiguration>('debug').internalConsoleOptions;
|
||||
if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
|
||||
@@ -911,8 +934,8 @@ export class DebugService implements debug.IDebugService {
|
||||
isBuiltin: adapter.extensionDescription.isBuiltin,
|
||||
launchJsonExists: root && !!this.configurationService.getValue<debug.IGlobalConfig>('launch', { resource: root.uri })
|
||||
});
|
||||
}).then(() => process, (error: any) => {
|
||||
if (error instanceof Error && error.message === 'Canceled') {
|
||||
}).then(() => process, (error: Error | string) => {
|
||||
if (errors.isPromiseCanceledError(error)) {
|
||||
// Do not show 'canceled' error messages to the user #7906
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -921,7 +944,7 @@ export class DebugService implements debug.IDebugService {
|
||||
/* __GDPR__
|
||||
"debugMisconfiguration" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"error": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"error": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
|
||||
@@ -940,14 +963,24 @@ export class DebugService implements debug.IDebugService {
|
||||
this.inDebugMode.reset();
|
||||
}
|
||||
|
||||
const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
|
||||
const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction];
|
||||
this.messageService.show(severity.Error, { message: errorMessage, actions });
|
||||
this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []);
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private showError(message: string, actions: IAction[] = []): TPromise<any> {
|
||||
const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
|
||||
actions.push(configureAction);
|
||||
return this.choiceService.choose(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), actions.length, true).then(choice => {
|
||||
if (choice < actions.length) {
|
||||
return actions[choice].run();
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
});
|
||||
}
|
||||
|
||||
private runPreLaunchTask(sessionId: string, root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
|
||||
if (!taskName) {
|
||||
return TPromise.as(null);
|
||||
@@ -1020,7 +1053,7 @@ export class DebugService implements debug.IDebugService {
|
||||
const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
|
||||
|
||||
return process.session.disconnect(true).then(() => {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost') && process.session.root) {
|
||||
return this.broadcastService.broadcast({
|
||||
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
|
||||
payload: [process.session.root.uri.fsPath]
|
||||
@@ -1031,15 +1064,17 @@ export class DebugService implements debug.IDebugService {
|
||||
setTimeout(() => {
|
||||
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
|
||||
let config = process.configuration;
|
||||
if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
|
||||
|
||||
const launch = process.session.root ? this.configurationManager.getLaunch(process.session.root.uri) : undefined;
|
||||
if (this.launchJsonChanged && launch) {
|
||||
this.launchJsonChanged = false;
|
||||
config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
|
||||
config = launch.getConfiguration(process.configuration.name) || config;
|
||||
// Take the type from the process since the debug extension might overwrite it #21316
|
||||
config.type = process.configuration.type;
|
||||
config.noDebug = process.configuration.noDebug;
|
||||
}
|
||||
config.__restart = restartData;
|
||||
this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
|
||||
this.startDebugging(launch, config).then(() => c(null), err => e(err));
|
||||
}, 300);
|
||||
});
|
||||
}).then(() => {
|
||||
@@ -1047,7 +1082,7 @@ export class DebugService implements debug.IDebugService {
|
||||
// Restart should preserve the focused process
|
||||
const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
|
||||
if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
|
||||
this.focusStackFrameAndEvaluate(null, restartedProcess);
|
||||
this.focusStackFrame(undefined, undefined, restartedProcess);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1090,7 +1125,7 @@ export class DebugService implements debug.IDebugService {
|
||||
});
|
||||
|
||||
this.model.removeProcess(session.getId());
|
||||
if (process && process.state !== debug.ProcessState.INACTIVE) {
|
||||
if (process) {
|
||||
process.inactive = true;
|
||||
this._onDidEndProcess.fire(process);
|
||||
}
|
||||
@@ -1098,7 +1133,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
|
||||
const focusedProcess = this.viewModel.focusedProcess;
|
||||
if (focusedProcess && focusedProcess.getId() === session.getId()) {
|
||||
this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(null);
|
||||
}
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Inactive);
|
||||
|
||||
@@ -1146,11 +1181,6 @@ export class DebugService implements debug.IDebugService {
|
||||
if (!session.readyForBreakpoints) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
if (this.textFileService.isDirty(modelUri)) {
|
||||
// Only send breakpoints for a file once it is not dirty #8077
|
||||
this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString());
|
||||
|
||||
@@ -1166,6 +1196,8 @@ export class DebugService implements debug.IDebugService {
|
||||
if (breakpointsToSend.length && !rawSource.adapterData) {
|
||||
rawSource.adapterData = breakpointsToSend[0].adapterData;
|
||||
}
|
||||
// Normalize all drive letters going out from vscode to debug adapters so we are consistent with our resolving #43959
|
||||
rawSource.path = normalizeDriveLetter(rawSource.path);
|
||||
|
||||
return session.setBreakpoints({
|
||||
source: rawSource,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IDebugService, IStackFrame } from 'vs/workbench/parts/debug/common/debu
|
||||
import { clipboard } from 'electron';
|
||||
|
||||
export class CopyValueAction extends Action {
|
||||
static ID = 'workbench.debug.viewlet.action.copyValue';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.copyValue';
|
||||
static LABEL = nls.localize('copyValue', "Copy Value");
|
||||
|
||||
constructor(id: string, label: string, private value: any, @IDebugService private debugService: IDebugService) {
|
||||
@@ -34,8 +34,25 @@ export class CopyValueAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyEvaluatePathAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.copyEvaluatePath';
|
||||
static LABEL = nls.localize('copyAsExpression', "Copy as Expression");
|
||||
|
||||
constructor(id: string, label: string, private value: any) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
if (this.value instanceof Variable) {
|
||||
clipboard.writeText(this.value.evaluateName);
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyAction extends Action {
|
||||
static ID = 'workbench.debug.action.copy';
|
||||
static readonly ID = 'workbench.debug.action.copy';
|
||||
static LABEL = nls.localize('copy', "Copy");
|
||||
|
||||
public run(): TPromise<any> {
|
||||
@@ -45,7 +62,7 @@ export class CopyAction extends Action {
|
||||
}
|
||||
|
||||
export class CopyAllAction extends Action {
|
||||
static ID = 'workbench.debug.action.copyAll';
|
||||
static readonly ID = 'workbench.debug.action.copyAll';
|
||||
static LABEL = nls.localize('copyAll', "Copy All");
|
||||
|
||||
constructor(id: string, label: string, private tree: ITree) {
|
||||
@@ -69,7 +86,7 @@ export class CopyAllAction extends Action {
|
||||
}
|
||||
|
||||
export class CopyStackTraceAction extends Action {
|
||||
static ID = 'workbench.action.debug.copyStackTrace';
|
||||
static readonly ID = 'workbench.action.debug.copyStackTrace';
|
||||
static LABEL = nls.localize('copyStackTrace', "Copy Call Stack");
|
||||
|
||||
public run(frame: IStackFrame): TPromise<any> {
|
||||
|
||||
@@ -12,9 +12,7 @@ import objects = require('vs/base/common/objects');
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import stdfork = require('vs/base/node/stdFork');
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution';
|
||||
@@ -26,6 +24,7 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { TerminalSupport } from 'vs/workbench/parts/debug/electron-browser/terminalSupport';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export interface SessionExitedEvent extends debug.DebugEvent {
|
||||
body: {
|
||||
@@ -72,7 +71,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
private adapter: Adapter,
|
||||
public customTelemetryService: ITelemetryService,
|
||||
public root: IWorkspaceFolder,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IOutputService private outputService: IOutputService,
|
||||
@ITerminalService private terminalService: ITerminalService,
|
||||
@@ -166,7 +165,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (error && error.sendTelemetry) {
|
||||
/* __GDPR__
|
||||
"debugProtocolErrorResponse" : {
|
||||
"error" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"error" : { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
@@ -183,7 +182,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (error && error.url) {
|
||||
const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info");
|
||||
return TPromise.wrapError<R>(errors.create(userMessage, {
|
||||
actions: [CloseAction, new Action('debug.moreInfo', label, null, true, () => {
|
||||
actions: [new Action('debug.moreInfo', label, null, true, () => {
|
||||
window.open(error.url);
|
||||
return TPromise.as(null);
|
||||
})]
|
||||
@@ -206,6 +205,9 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (event.event === 'initialized') {
|
||||
this.readyForBreakpoints = true;
|
||||
this._onDidInitialize.fire(event);
|
||||
} else if (event.event === 'capabilities' && event.body) {
|
||||
const capabilites = (<DebugProtocol.CapabilitiesEvent>event).body.capabilities;
|
||||
this._capabilities = objects.mixin(this._capabilities, capabilites);
|
||||
} else if (event.event === 'stopped') {
|
||||
this.emittedStopped = true;
|
||||
this._onDidStop.fire(<DebugProtocol.StoppedEvent>event);
|
||||
@@ -524,7 +526,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
}
|
||||
|
||||
protected onServerError(err: Error): void {
|
||||
this.messageService.show(severity.Error, nls.localize('stoppingDebugAdapter', "{0}. Stopping the debug adapter.", err.message));
|
||||
this.notificationService.error(nls.localize('stoppingDebugAdapter', "{0}. Stopping the debug adapter.", err.message));
|
||||
this.stopServer().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
@@ -532,7 +534,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
this.serverProcess = null;
|
||||
this.cachedInitServer = null;
|
||||
if (!this.disconnected) {
|
||||
this.messageService.show(severity.Error, nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"));
|
||||
this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"));
|
||||
}
|
||||
this.onEvent({ event: 'exit', type: 'event', seq: 0 });
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ITree, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -41,15 +41,16 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { clipboard } from 'electron';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
const replTreeOptions: ITreeOptions = {
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel")
|
||||
};
|
||||
|
||||
const HISTORY_STORAGE_KEY = 'debug.repl.history';
|
||||
@@ -82,6 +83,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
private actions: IAction[];
|
||||
private dimension: Dimension;
|
||||
private replInputHeight: number;
|
||||
private model: ITextModel;
|
||||
|
||||
constructor(
|
||||
@debug.IDebugService private debugService: debug.IDebugService,
|
||||
@@ -91,8 +93,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
@IPanelService private panelService: IPanelService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IListService private listService: IListService
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(debug.REPL_ID, telemetryService, themeService);
|
||||
|
||||
@@ -118,7 +119,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
this.refreshTimeoutHandle = null;
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh().then(() => {
|
||||
if (previousScrollPosition === 1 || previousScrollPosition === 0) {
|
||||
if (previousScrollPosition === 1) {
|
||||
// Only scroll if we were scrolled all the way down before tree refreshed #10486
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
@@ -134,15 +135,15 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
this.createReplInput(this.container);
|
||||
|
||||
this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService, this.replInput), MenuId.DebugConsoleContext, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change, to preserve focus behaviour in input field */ });
|
||||
controller.toFocusOnClick = this.replInput;
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new ReplExpressionsDataSource(),
|
||||
renderer: this.renderer,
|
||||
accessibilityProvider: new ReplExpressionsAccessibilityProvider(),
|
||||
controller
|
||||
}, replTreeOptions, this.contextKeyService, this.listService, this.themeService);
|
||||
}, replTreeOptions);
|
||||
|
||||
if (!Repl.HISTORY) {
|
||||
Repl.HISTORY = new ReplHistory(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')));
|
||||
@@ -151,6 +152,17 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
return this.tree.setInput(this.debugService.getModel());
|
||||
}
|
||||
|
||||
public setVisible(visible: boolean): TPromise<void> {
|
||||
if (!visible) {
|
||||
dispose(this.model);
|
||||
} else {
|
||||
this.model = this.modelService.createModel('', null, uri.parse(`${debug.DEBUG_SCHEME}:input`));
|
||||
this.replInput.setModel(this.model);
|
||||
}
|
||||
|
||||
return super.setVisible(visible);
|
||||
}
|
||||
|
||||
private createReplInput(container: HTMLElement): void {
|
||||
this.replInputContainer = dom.append(container, $('.repl-input-wrapper'));
|
||||
|
||||
@@ -165,12 +177,10 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, scopedContextKeyService], [IPrivateReplService, this]));
|
||||
this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions());
|
||||
const model = this.modelService.createModel('', null, uri.parse(`${debug.DEBUG_SCHEME}:input`));
|
||||
this.replInput.setModel(model);
|
||||
|
||||
modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
const word = this.replInput.getModel().getWordAtPosition(position);
|
||||
const overwriteBefore = word ? word.word.length : 0;
|
||||
const text = this.replInput.getModel().getLineContent(position.lineNumber);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
@@ -17,8 +17,8 @@ import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer
|
||||
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Model, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { ClearReplAction, ReplCollapseAllAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -417,7 +417,7 @@ export class ReplExpressionsAccessibilityProvider implements IAccessibilityProvi
|
||||
|
||||
export class ReplExpressionsActionProvider implements IActionProvider {
|
||||
|
||||
constructor(private instantiationService: IInstantiationService) {
|
||||
constructor(private instantiationService: IInstantiationService, private toFocus: { focus(): void }) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -437,6 +437,8 @@ export class ReplExpressionsActionProvider implements IActionProvider {
|
||||
const actions: IAction[] = [];
|
||||
actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL));
|
||||
actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree));
|
||||
actions.push(new ReplCollapseAllAction(tree, this.toFocus));
|
||||
actions.push(new Separator());
|
||||
actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL));
|
||||
|
||||
return TPromise.as(actions);
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { registerColor, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme';
|
||||
import { addClass, removeClass } from 'vs/base/browser/dom';
|
||||
|
||||
// colors for theming
|
||||
|
||||
export const STATUS_BAR_DEBUGGING_BACKGROUND = registerColor('statusBar.debuggingBackground', {
|
||||
dark: '#CC6633',
|
||||
light: '#CC6633',
|
||||
hc: '#CC6633'
|
||||
}, localize('statusBarDebuggingBackground', "Status bar background color when a program is being debugged. The status bar is shown in the bottom of the window"));
|
||||
|
||||
export const STATUS_BAR_DEBUGGING_FOREGROUND = registerColor('statusBar.debuggingForeground', {
|
||||
dark: STATUS_BAR_FOREGROUND,
|
||||
light: STATUS_BAR_FOREGROUND,
|
||||
hc: STATUS_BAR_FOREGROUND
|
||||
}, localize('statusBarDebuggingForeground', "Status bar foreground color when a program is being debugged. The status bar is shown in the bottom of the window"));
|
||||
|
||||
export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBorder', {
|
||||
dark: STATUS_BAR_BORDER,
|
||||
light: STATUS_BAR_BORDER,
|
||||
hc: STATUS_BAR_BORDER
|
||||
}, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window"));
|
||||
|
||||
export class StatusBarColorProvider extends Themable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IPartService private partService: IPartService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.debugService.onDidChangeState(state => this.updateStyles()));
|
||||
this.toUnbind.push(this.contextService.onDidChangeWorkbenchState(state => this.updateStyles()));
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = this.partService.getContainer(Parts.STATUSBAR_PART);
|
||||
if (this.isDebugging()) {
|
||||
addClass(container, 'debugging');
|
||||
} else {
|
||||
removeClass(container, 'debugging');
|
||||
}
|
||||
|
||||
container.style.backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
|
||||
container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND));
|
||||
|
||||
const borderColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_DEBUGGING_BORDER, STATUS_BAR_BORDER)) || this.getColor(contrastBorder);
|
||||
container.style.borderTopWidth = borderColor ? '1px' : null;
|
||||
container.style.borderTopStyle = borderColor ? 'solid' : null;
|
||||
container.style.borderTopColor = borderColor;
|
||||
}
|
||||
|
||||
private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string {
|
||||
|
||||
// Not debugging
|
||||
if (!this.isDebugging()) {
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||
return normalColor;
|
||||
}
|
||||
|
||||
return noFolderColor;
|
||||
}
|
||||
|
||||
// Debugging
|
||||
return debuggingColor;
|
||||
}
|
||||
|
||||
private isDebugging(): boolean {
|
||||
if (this.debugService.state === State.Inactive || this.debugService.state === State.Initializing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isRunningWithoutDebug()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private isRunningWithoutDebug(): boolean {
|
||||
const process = this.debugService.getViewModel().focusedProcess;
|
||||
|
||||
return process && process.configuration && process.configuration.noDebug;
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const statusBarItemDebuggingForeground = theme.getColor(STATUS_BAR_DEBUGGING_FOREGROUND);
|
||||
if (statusBarItemDebuggingForeground) {
|
||||
collector.addRule(`.monaco-workbench > .part.statusbar.debugging > .statusbar-item .mask-icon { background-color: ${statusBarItemDebuggingForeground} !important; }`);
|
||||
}
|
||||
});
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import cp = require('child_process');
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITerminalService, ITerminalInstance, ITerminalConfiguration } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
@@ -24,11 +25,6 @@ export class TerminalSupport {
|
||||
return nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {});
|
||||
}
|
||||
|
||||
let delay = 0;
|
||||
if (!TerminalSupport.integratedTerminalInstance) {
|
||||
TerminalSupport.integratedTerminalInstance = terminalService.createInstance({ name: args.title || nls.localize('debug.terminal.title', "debuggee") });
|
||||
delay = 2000; // delay the first sendText so that the newly created terminal is ready.
|
||||
}
|
||||
if (!TerminalSupport.terminalDisposedListener) {
|
||||
// React on terminal disposed and check if that is the debug terminal #12956
|
||||
TerminalSupport.terminalDisposedListener = terminalService.onInstanceDisposed(terminal => {
|
||||
@@ -37,22 +33,49 @@ export class TerminalSupport {
|
||||
}
|
||||
});
|
||||
}
|
||||
terminalService.setActiveInstance(TerminalSupport.integratedTerminalInstance);
|
||||
|
||||
let t = TerminalSupport.integratedTerminalInstance;
|
||||
if ((t && this.isBusy(t)) || !t) {
|
||||
t = terminalService.createInstance({ name: args.title || nls.localize('debug.terminal.title', "debuggee") });
|
||||
TerminalSupport.integratedTerminalInstance = t;
|
||||
}
|
||||
terminalService.setActiveInstance(t);
|
||||
terminalService.showPanel(true);
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
const command = this.prepareCommand(args, configurationService);
|
||||
t.sendText(command, true);
|
||||
|
||||
setTimeout(() => {
|
||||
if (TerminalSupport.integratedTerminalInstance) {
|
||||
const command = this.prepareCommand(args, configurationService);
|
||||
TerminalSupport.integratedTerminalInstance.sendText(command, true);
|
||||
c(void 0);
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
private static isBusy(t: ITerminalInstance): boolean {
|
||||
if (t.processId) {
|
||||
try {
|
||||
// if shell has at least one child process, assume that shell is busy
|
||||
if (platform.isWindows) {
|
||||
const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']);
|
||||
if (result.stdout) {
|
||||
const pids = result.stdout.toString().split('\r\n');
|
||||
if (!pids.some(p => parseInt(p) === t.processId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
e(new Error(nls.localize('debug.terminal.not.available.error', "Integrated terminal not available")));
|
||||
const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(t.processId)]);
|
||||
if (result.stdout) {
|
||||
const r = result.stdout.toString().trim();
|
||||
if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// silently ignore
|
||||
}
|
||||
}
|
||||
// fall back to safe side
|
||||
return true;
|
||||
}
|
||||
|
||||
private static prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, configurationService: IConfigurationService): string {
|
||||
@@ -94,7 +117,8 @@ export class TerminalSupport {
|
||||
|
||||
quote = (s: string) => {
|
||||
s = s.replace(/\'/g, '\'\'');
|
||||
return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s;
|
||||
return `'${s}'`;
|
||||
//return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s;
|
||||
};
|
||||
|
||||
if (args.cwd) {
|
||||
|
||||
@@ -7,8 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler, sequence } from 'vs/base/common/async';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, State, CONTEXT_VARIABLES_FOCUSED, IExpression } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -17,19 +16,19 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -40,6 +39,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
private settings: any;
|
||||
private expandedElements: any[];
|
||||
private needsRefresh: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
@@ -47,11 +47,9 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.expandedElements = [];
|
||||
@@ -63,8 +61,6 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
this.expandedElements = expanded;
|
||||
}
|
||||
|
||||
// Always clear tree highlight to avoid ending up in a broken state #12203
|
||||
this.tree.clearHighlight();
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh().then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
@@ -88,16 +84,15 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new VariablesDataSource(),
|
||||
renderer: this.instantiationService.createInstance(VariablesRenderer),
|
||||
accessibilityProvider: new VariablesAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext)
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext, { openMode: OpenMode.SINGLE_CLICK })
|
||||
}, {
|
||||
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -106,7 +101,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
this.tree.setInput(viewModel);
|
||||
|
||||
const collapseAction = new CollapseAction(this.tree, false, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions(prepareActions([collapseAction]))();
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
|
||||
this.disposables.push(viewModel.onDidFocusStackFrame(sf => {
|
||||
if (!this.isVisible() || !this.isExpanded()) {
|
||||
@@ -127,21 +122,19 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
}));
|
||||
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Variable)) {
|
||||
return;
|
||||
if (expression instanceof Variable) {
|
||||
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.tree.refresh(expression, false).then(() => {
|
||||
this.tree.setHighlight(expression);
|
||||
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
public setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
if (expanded && this.needsRefresh) {
|
||||
@@ -187,6 +180,7 @@ class VariablesActionProvider implements IActionProvider {
|
||||
const variable = <Variable>element;
|
||||
actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, this.debugService));
|
||||
actions.push(new CopyEvaluatePathAction(CopyEvaluatePathAction.ID, CopyEvaluatePathAction.LABEL, variable));
|
||||
actions.push(new Separator());
|
||||
actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -20,17 +19,16 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { DefaultDragAndDrop, OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
@@ -39,7 +37,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
|
||||
private static readonly MEMENTO = 'watchexpressionsview.memento';
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
private toReveal: IExpression;
|
||||
private treeContainer: HTMLElement;
|
||||
private settings: any;
|
||||
private needsRefresh: boolean;
|
||||
|
||||
@@ -48,26 +46,15 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IListService private listService: IListService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof Expression) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
}));
|
||||
|
||||
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh().done(() => {
|
||||
return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true);
|
||||
}, errors.onUnexpectedError);
|
||||
this.tree.refresh().done(undefined, errors.onUnexpectedError);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
@@ -76,17 +63,16 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
const actionProvider = new WatchExpressionsActionProvider(this.debugService, this.keybindingService);
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
dataSource: new WatchExpressionsDataSource(),
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new WatchExpressionsDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(WatchExpressionsRenderer),
|
||||
accessibilityProvider: new WatchExpressionsAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, openMode: OpenMode.SINGLE_CLICK }),
|
||||
dnd: new WatchExpressionsDragAndDrop(this.debugService)
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -95,7 +81,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService);
|
||||
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
|
||||
const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService);
|
||||
this.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
|
||||
this.toolbar.setActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])();
|
||||
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
if (!this.isExpanded() || !this.isVisible()) {
|
||||
@@ -103,28 +89,35 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.refresh().done(() => {
|
||||
return we instanceof Expression ? this.tree.reveal(we) : TPromise.as(true);
|
||||
}, errors.onUnexpectedError);
|
||||
}));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => {
|
||||
if (!this.isExpanded() || !this.isVisible()) {
|
||||
this.needsRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
|
||||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
this.toReveal = we;
|
||||
}));
|
||||
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Expression)) {
|
||||
return;
|
||||
if (expression instanceof Expression) {
|
||||
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.tree.refresh(expression, false).then(() => {
|
||||
this.tree.setHighlight(expression);
|
||||
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
public setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
if (expanded && this.needsRefresh) {
|
||||
@@ -200,6 +193,10 @@ class WatchExpressionsActionProvider implements IActionProvider {
|
||||
|
||||
class WatchExpressionsDataSource implements IDataSource {
|
||||
|
||||
constructor(private debugService: IDebugService) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public getId(tree: ITree, element: any): string {
|
||||
return element.getId();
|
||||
}
|
||||
@@ -215,7 +212,9 @@ class WatchExpressionsDataSource implements IDataSource {
|
||||
|
||||
public getChildren(tree: ITree, element: any): TPromise<any> {
|
||||
if (element instanceof Model) {
|
||||
return TPromise.as((<Model>element).getWatchExpressions());
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
return TPromise.join(element.getWatchExpressions().map(we =>
|
||||
we.name ? we.evaluate(viewModel.focusedProcess, viewModel.focusedStackFrame, 'watch').then(() => we) : TPromise.as(we)));
|
||||
}
|
||||
|
||||
let expression = <Expression>element;
|
||||
@@ -291,7 +290,7 @@ class WatchExpressionsRenderer implements IRenderer {
|
||||
|
||||
private renderWatchExpression(tree: ITree, watchExpression: IExpression, data: IWatchExpressionTemplateData): void {
|
||||
let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
|
||||
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
|
||||
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId())) {
|
||||
renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, watchExpression, data.expression, {
|
||||
initialValue: watchExpression.name,
|
||||
placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),
|
||||
@@ -344,6 +343,10 @@ class WatchExpressionsController extends BaseDebugController {
|
||||
const expression = <IExpression>element;
|
||||
this.debugService.getViewModel().setSelectedExpression(expression);
|
||||
return true;
|
||||
} else if (element instanceof Model && event.detail === 2) {
|
||||
// Double click in watch panel triggers to add a new watch expression
|
||||
this.debugService.addWatchExpression();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onLeftClick(tree, element, event);
|
||||
|
||||
Reference in New Issue
Block a user